1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2024 Google LLC
4  * Written by Simon Glass <sjg@chromium.org>
5  */
6 
7 #include <membuf.h>
8 #include <os.h>
9 #include <rand.h>
10 #include <string.h>
11 #include <test/lib.h>
12 #include <test/test.h>
13 #include <test/ut.h>
14 
15 #define TEST_SIZE	16
16 #define TEST_COUNT	10000
17 
membuf_zero(struct membuf * mb)18 static void membuf_zero(struct membuf *mb)
19 {
20 	memset(mb->start, '\0', mb->end - mb->start);
21 }
22 
membuf_check(struct unit_test_state * uts,struct membuf * mb,int value)23 static int membuf_check(struct unit_test_state *uts, struct membuf *mb,
24 			int value)
25 {
26 	/* head is out of range */
27 	ut_assert(!(mb->head < mb->start || mb->head >= mb->end));
28 
29 	/* tail is out of range */
30 	ut_assert(!(mb->tail < mb->start || mb->tail >= mb->end));
31 
32 	return 0;
33 }
34 
35 /* write from 1 to test_size bytes, and check they come back OK */
lib_test_membuf_one(struct unit_test_state * uts)36 static int lib_test_membuf_one(struct unit_test_state *uts)
37 {
38 	char in[TEST_SIZE * 2], out[TEST_SIZE * 2];
39 	struct membuf mb;
40 	int size, ret, test_size, i;
41 
42 	ut_assertok(membuf_new(&mb, TEST_SIZE));
43 
44 	/* setup in test */
45 	for (i = 0; i < TEST_SIZE; i++) {
46 		in[i] = (i & 63) + '0';
47 		in[i + TEST_SIZE] = in[i];
48 	}
49 
50 	test_size = TEST_SIZE;
51 
52 	for (i = 1; i < TEST_COUNT; i++) {
53 		membuf_zero(&mb);
54 		size = rand() % test_size;
55 
56 		// now write patterns and check they come back OK
57 		ret = membuf_put(&mb, in, 0);
58 		ret = membuf_put(&mb, in, size);
59 		ut_asserteq(size, ret);
60 
61 		ret = membuf_put(&mb, in, 0);
62 		ut_assertok(membuf_check(uts, &mb, i));
63 
64 		ret = membuf_get(&mb, out, 0);
65 		ret = membuf_get(&mb, out, size);
66 		ut_asserteq(size, ret);
67 
68 		ret = membuf_get(&mb, out, 0);
69 		ut_assertok(membuf_check(uts, &mb, i));
70 
71 		ut_asserteq_mem(in, out, size);
72 	}
73 
74 	return 0;
75 }
76 LIB_TEST(lib_test_membuf_one, 0);
77 
78 /* write random number of bytes, and check they come back OK */
lib_test_membuf_random(struct unit_test_state * uts)79 static int lib_test_membuf_random(struct unit_test_state *uts)
80 {
81 	char in[TEST_SIZE * 2];
82 	char buf[TEST_SIZE * 2];
83 	struct membuf mb;
84 	int size, ret, test_size, i;
85 	char *inptr, *outptr;
86 	int max_avail, min_free;
87 
88 	ut_assertok(membuf_new(&mb, TEST_SIZE));
89 
90 	for (i = 0; i < TEST_SIZE; i++) {
91 		in[i] = (i & 63) + '0';
92 		in[i + TEST_SIZE] = in[i];
93 	}
94 
95 	test_size = TEST_SIZE;
96 
97 	inptr = in;
98 	outptr = in;
99 	min_free = TEST_COUNT;
100 	max_avail = 0;
101 	membuf_zero(&mb);
102 	for (i = 0; i < TEST_COUNT; i++) {
103 		size = rand() % test_size;
104 
105 		if (membuf_free(&mb) < min_free)
106 			min_free = membuf_free(&mb);
107 
108 		ret = membuf_put(&mb, inptr, size);
109 		ut_assertok(membuf_check(uts, &mb, i));
110 		inptr += ret;
111 		if (inptr >= in + TEST_SIZE)
112 			inptr -= TEST_SIZE;
113 
114 		size = rand() % (test_size - 1);
115 
116 		if (membuf_avail(&mb) > max_avail)
117 			max_avail = membuf_avail(&mb);
118 
119 		ret = membuf_get(&mb, buf, size);
120 		ut_assertok(membuf_check(uts, &mb, i));
121 		ut_asserteq_mem(buf, outptr, ret);
122 
123 		outptr += ret;
124 		if (outptr >= in + TEST_SIZE)
125 			outptr -= TEST_SIZE;
126 	}
127 
128 	return 0;
129 }
130 LIB_TEST(lib_test_membuf_random, 0);
131 
132 /* test membuf_extend() with split segments */
lib_test_membuf_extend(struct unit_test_state * uts)133 static int lib_test_membuf_extend(struct unit_test_state *uts)
134 {
135 	char in[TEST_SIZE * 2];
136 	char buf[TEST_SIZE * 2];
137 	struct membuf mb;
138 	int ret, test_size, i, cur;
139 	char *data;
140 
141 	ut_assertok(membuf_new(&mb, TEST_SIZE));
142 
143 	for (i = 0; i < TEST_SIZE; i++) {
144 		in[i] = (i & 63) + '0';
145 		in[i + TEST_SIZE] = in[i];
146 	}
147 
148 	test_size = TEST_SIZE - 1;
149 
150 	for (cur = 0; cur <= test_size; cur++) {
151 		ut_assertok(membuf_new(&mb, TEST_SIZE));
152 
153 		membuf_zero(&mb);
154 
155 		/*
156 		 * add some bytes, then remove them - this will force the membuf
157 		 * to have data split into two segments when we fill it
158 		 */
159 		ret = membuf_putraw(&mb, TEST_SIZE / 2, true, &data);
160 		membuf_getraw(&mb, ret, true, &data);
161 		ut_asserteq(TEST_SIZE / 2, ret);
162 
163 		/* fill it */
164 		ret = membuf_put(&mb, in, cur);
165 		ut_assertok(membuf_check(uts, &mb, cur));
166 		ut_asserteq(cur, ret);
167 
168 		/* extend the buffer */
169 		ut_assertok(membuf_extend_by(&mb, TEST_SIZE, -1));
170 		ut_assertok(membuf_check(uts, &mb, cur));
171 
172 		/* check our data is still there */
173 		ret = membuf_get(&mb, buf, TEST_SIZE * 2);
174 		ut_assertok(membuf_check(uts, &mb, cur));
175 		ut_asserteq(cur, ret);
176 		ut_asserteq_mem(in, buf, cur);
177 		membuf_uninit(&mb);
178 	}
179 
180 	return 0;
181 }
182 LIB_TEST(lib_test_membuf_extend, 0);
183 
184 /* test membuf_readline() with generated data */
lib_test_membuf_readline(struct unit_test_state * uts)185 static int lib_test_membuf_readline(struct unit_test_state *uts)
186 {
187 	char *buf;
188 	int size, cur, i, ret, readptr, cmpptr;
189 	struct membuf mb;
190 	char *data;
191 	char str[256];
192 	char *s;
193 
194 	ut_assertok(membuf_new(&mb, 1024));
195 	membuf_zero(&mb);
196 
197 	/* Use the README as test data */
198 	ut_assertok(os_read_file("README", (void **)&buf, &size));
199 
200 	cur = 0;
201 	readptr = 0;
202 	cmpptr = 0;
203 	for (i = 0; i < 100000; i++, cur += 1) {
204 		/* fill the buffer with up to 'cur' bytes */
205 		ret = membuf_putraw(&mb, cur, false, &data);
206 
207 		if (ret > 0) {
208 			int can_read = min(ret, size - readptr);
209 
210 			memcpy(data, &buf[readptr], can_read);
211 			readptr += can_read;
212 
213 			membuf_putraw(&mb, can_read, true, &data);
214 			ut_assertok(membuf_check(uts, &mb, i));
215 		}
216 
217 		/* read a line and compare */
218 		ret = membuf_readline(&mb, str, 256, 0, true);
219 		ut_assertok(membuf_check(uts, &mb, i));
220 		if (ret) {
221 			char *ptr;
222 
223 			s = &buf[cmpptr];
224 			ptr = strchr(s, '\n');
225 			*ptr = '\0';
226 
227 			ut_asserteq_str(s, str);
228 			cmpptr += strlen(s) + 1;
229 			*ptr = '\n';
230 		} else {
231 			ut_assert(membuf_free(&mb));
232 		}
233 	}
234 	membuf_dispose(&mb);
235 	os_free(buf);
236 
237 	return 0;
238 }
239 LIB_TEST(lib_test_membuf_readline, 0);
240