1 /*
2  * Copyright (c) 2017 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /** @file
8  *  @brief Interactive shell test suite
9  *
10  */
11 
12 #include <zephyr/kernel.h>
13 #include <zephyr/ztest.h>
14 
15 #include <zephyr/shell/shell_history.h>
16 
17 #define HIST_BUF_SIZE 160
18 Z_SHELL_HISTORY_DEFINE(history, HIST_BUF_SIZE);
19 
init_test_buf(uint8_t * buf,size_t len,uint8_t offset)20 static void init_test_buf(uint8_t *buf, size_t len, uint8_t offset)
21 {
22 	for (int i = 0; i < len; i++) {
23 		buf[i] = offset + i;
24 	}
25 }
26 
27 /**
28  * Function tests getting line from history and compares it against expected
29  * result.
30  */
test_get(bool ok,bool up,uint8_t * exp_buf,uint16_t exp_len)31 static void test_get(bool ok, bool up, uint8_t *exp_buf, uint16_t exp_len)
32 {
33 	bool res;
34 	uint8_t out_buf[HIST_BUF_SIZE];
35 	uint16_t out_len;
36 
37 	out_len = sizeof(out_buf);
38 
39 	res = z_shell_history_get(&history, up, out_buf, &out_len);
40 
41 	if (ok) {
42 		zassert_true(res, "history should contain one entry.\n");
43 
44 		zassert_equal(out_len, exp_len, "Unexpected entry length.\n");
45 		if (out_len) {
46 			zassert_equal(memcmp(out_buf, exp_buf, out_len), 0,
47 				"Expected equal buffers.\n");
48 		}
49 	} else {
50 		zassert_false(res, "History should return nothing.\n");
51 	}
52 }
53 
54 /* Test put line to history and get it.
55  *
56  * Test steps:
57  * - initialize history.
58  * - put line to the history.
59  * - read line and verify that it is the one that was put.
60  */
ZTEST(shell_test,test_history_add_get)61 ZTEST(shell_test, test_history_add_get)
62 {
63 	uint8_t exp_buf[HIST_BUF_SIZE];
64 
65 	init_test_buf(exp_buf, sizeof(exp_buf), 0);
66 
67 	z_shell_history_init(&history);
68 
69 	test_get(false, true, NULL, 0);
70 
71 	z_shell_history_put(&history, exp_buf, 20);
72 
73 	test_get(true, true, exp_buf, 20);
74 
75 	z_shell_history_purge(&history);
76 }
77 
78 /* Test verifies that after purging there is no line in the history. */
ZTEST(shell_test,test_history_purge)79 ZTEST(shell_test, test_history_purge)
80 {
81 	uint8_t exp_buf[HIST_BUF_SIZE];
82 
83 	init_test_buf(exp_buf, sizeof(exp_buf), 0);
84 
85 	z_shell_history_init(&history);
86 
87 	z_shell_history_put(&history, exp_buf, 20);
88 	z_shell_history_put(&history, exp_buf, 20);
89 
90 	z_shell_history_purge(&history);
91 
92 	test_get(false, true, NULL, 0);
93 }
94 
95 /* Test browsing history.
96  *
97  * Test steps:
98  * - initialize history.
99  * - put  lines 1,2,3 to history.
100  * - get in up direction a line and verify that it's the last one added (3).
101  * - get next line in up direction and verify that it's line 2.
102  * - get next line in up direction and verify that it's line 1.
103  * - get next line in down direction and verify that it's line 2.
104  * - get next line in up direction and verify that it's line 1.
105  * - get next line in down direction and verify that it's line 2.
106  * - get next line in down direction and verify that it's line 3.
107  * - attempt to get next line in down direction and verify that there is no
108  *   line.
109  */
ZTEST(shell_test,test_history_get_up_and_down)110 ZTEST(shell_test, test_history_get_up_and_down)
111 {
112 	uint8_t exp1_buf[HIST_BUF_SIZE];
113 	uint8_t exp2_buf[HIST_BUF_SIZE];
114 	uint8_t exp3_buf[HIST_BUF_SIZE];
115 
116 	init_test_buf(exp1_buf, sizeof(exp1_buf), 0);
117 	init_test_buf(exp2_buf, sizeof(exp2_buf), 10);
118 	init_test_buf(exp3_buf, sizeof(exp3_buf), 20);
119 
120 	z_shell_history_init(&history);
121 
122 	z_shell_history_put(&history, exp1_buf, 20);
123 	z_shell_history_put(&history, exp2_buf, 15);
124 	z_shell_history_put(&history, exp3_buf, 20);
125 
126 	test_get(true, true, exp3_buf, 20); /* up - 3*/
127 	test_get(true, true, exp2_buf, 15); /* up - 2*/
128 	test_get(true, true, exp1_buf, 20); /* up - 1*/
129 	test_get(true, false, exp2_buf, 15); /* down - 2 */
130 	test_get(true, true, exp1_buf, 20);  /* up - 1*/
131 	test_get(true, false, exp2_buf, 15); /* down - 2 */
132 	test_get(true, false, exp3_buf, 20); /* down - 3 */
133 	test_get(false, false, NULL, 0); /* down - nothing */
134 
135 	z_shell_history_purge(&history);
136 }
137 
138 /* Function for getting maximal buffer size that can be stored in the history */
get_max_buffer_len(void)139 static int get_max_buffer_len(void)
140 {
141 	uint8_t buf[HIST_BUF_SIZE];
142 	uint8_t out_buf[HIST_BUF_SIZE];
143 	int len = sizeof(buf);
144 	uint16_t out_len;
145 
146 	z_shell_history_init(&history);
147 
148 	do {
149 		z_shell_history_put(&history, buf, len);
150 		out_len = sizeof(out_buf);
151 		if (z_shell_history_get(&history, true, out_buf, &out_len)) {
152 			z_shell_history_purge(&history);
153 			break;
154 		}
155 	} while (len--);
156 
157 	return len;
158 }
159 
160 /* Test verifies that line that cannot fit into history buffer is not stored.
161  *
162  * Test steps:
163  * - initialize history.
164  * - put buffer that is bigger than history overall capacity.
165  * - verify that history is empty.
166  * - put short line followed by line that is close to max.
167  * - verify that long line evicted first line from history.
168  */
ZTEST(shell_test,test_too_long_line_not_stored)169 ZTEST(shell_test, test_too_long_line_not_stored)
170 {
171 	uint8_t exp1_buf[HIST_BUF_SIZE];
172 	int max_len = get_max_buffer_len();
173 
174 	init_test_buf(exp1_buf, sizeof(exp1_buf), 0);
175 	z_shell_history_init(&history);
176 
177 	z_shell_history_put(&history, exp1_buf, max_len + 1);
178 
179 	/*validate that nothing is stored */
180 	test_get(false, true, NULL, 0); /* empty */
181 
182 	z_shell_history_put(&history, exp1_buf, 20);
183 	z_shell_history_put(&history, exp1_buf, max_len - 10);
184 
185 	/* Test that long entry evicts older entry. */
186 	test_get(true, true, exp1_buf, max_len - 10);
187 	test_get(false, true, NULL, 0); /* only one entry */
188 
189 	z_shell_history_purge(&history);
190 }
191 
192 /* Test verifies that same line as the previous one is not stored in the
193  * history.
194  *
195  * Test steps:
196  * - initialize history.
197  * - put same line twice.
198  * - verify that only one line is in the history.
199  */
ZTEST(shell_test,test_no_duplicates_in_a_row)200 ZTEST(shell_test, test_no_duplicates_in_a_row)
201 {
202 	uint8_t exp1_buf[HIST_BUF_SIZE];
203 
204 	init_test_buf(exp1_buf, sizeof(exp1_buf), 0);
205 	z_shell_history_init(&history);
206 
207 	z_shell_history_put(&history, exp1_buf, 20);
208 	z_shell_history_put(&history, exp1_buf, 20);
209 
210 	test_get(true, true, exp1_buf, 20);
211 	/* only one line stored. */
212 	test_get(false, true, NULL, 0);
213 
214 	z_shell_history_purge(&history);
215 }
216 
217 /* Test storing long lines in the history.
218  *
219  * * Test steps:
220  * - initialize history.
221  * - Put max length line 1 in history.
222  * - Verify that it is present.
223  * - Put max length line 2 in history.
224  * - Verify that line 2 is present and line 1 was evicted.
225  * - Put max length line 3 in history.
226  * - Verify that line 3 is present and line 2 was evicted.
227  */
ZTEST(shell_test,test_storing_long_buffers)228 ZTEST(shell_test, test_storing_long_buffers)
229 {
230 	uint8_t exp1_buf[HIST_BUF_SIZE];
231 	uint8_t exp2_buf[HIST_BUF_SIZE];
232 	uint8_t exp3_buf[HIST_BUF_SIZE];
233 	int max_len = get_max_buffer_len();
234 
235 	init_test_buf(exp1_buf, sizeof(exp1_buf), 0);
236 	init_test_buf(exp2_buf, sizeof(exp2_buf), 10);
237 	init_test_buf(exp3_buf, sizeof(exp3_buf), 20);
238 
239 	z_shell_history_init(&history);
240 
241 	z_shell_history_put(&history, exp1_buf, max_len);
242 	test_get(true, true, exp1_buf, max_len);
243 	test_get(false, true, NULL, 0); /* only one entry */
244 
245 	z_shell_history_put(&history, exp2_buf, max_len);
246 	test_get(true, true, exp2_buf, max_len);
247 	test_get(false, true, NULL, 0); /* only one entry */
248 
249 	z_shell_history_put(&history, exp3_buf, max_len);
250 	test_get(true, true, exp3_buf, max_len);
251 	test_get(false, true, NULL, 0); /* only one entry */
252 
253 	z_shell_history_purge(&history);
254 }
255 
256 ZTEST_SUITE(shell_test, NULL, NULL, NULL, NULL, NULL);
257