1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * |
|
12 * 2. Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
26 * POSSIBILITY OF SUCH DAMAGE. |
|
27 */ |
|
28 |
|
29 #include "cx/buffer.h" |
|
30 |
|
31 #include <gtest/gtest.h> |
|
32 #include "util_allocator.h" |
|
33 |
|
34 class BufferWrite : public ::testing::Test { |
|
35 protected: |
|
36 CxBuffer buf{}, target{}; |
|
37 |
|
38 void SetUp() override { |
|
39 cxBufferInit(&target, NULL, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); |
|
40 cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); |
|
41 buf.capacity = 8; // artificially reduce capacity to check OOB writes |
|
42 memset(buf.space, 0, 16); |
|
43 memcpy(buf.space, "prep", 4); |
|
44 buf.size = buf.pos = 4; |
|
45 } |
|
46 |
|
47 void TearDown() override { |
|
48 cxBufferDestroy(&buf); |
|
49 cxBufferDestroy(&target); |
|
50 } |
|
51 |
|
52 void enableFlushing() { |
|
53 buf.flush_target = ⌖ |
|
54 buf.flush_func = reinterpret_cast<cx_write_func>(cxBufferWrite); |
|
55 buf.flush_blkmax = 1; |
|
56 } |
|
57 }; |
|
58 |
|
59 static size_t mock_write_limited_rate( |
|
60 void const *ptr, |
|
61 size_t size, |
|
62 __attribute__((unused)) size_t nitems, |
|
63 CxBuffer *buffer |
|
64 ) { |
|
65 // simulate limited target drain capacity |
|
66 static bool full = false; |
|
67 if (full) { |
|
68 full = false; |
|
69 return 0; |
|
70 } else { |
|
71 full = true; |
|
72 return cxBufferWrite(ptr, size, nitems > 2 ? 2 : nitems, buffer); |
|
73 } |
|
74 } |
|
75 |
|
76 TEST_F(BufferWrite, SizeOneFit) { |
|
77 const char *data = "test"; |
|
78 CX_TEST_ASSERT(buf.capacity == 8); |
|
79 CX_TEST_ASSERT(buf.pos == 4); |
|
80 CX_TEST_ASSERT(buf.size == 4); |
|
81 size_t written = cxBufferWrite(data, 1, 4, &buf); |
|
82 CX_TEST_ASSERT(written == 4); |
|
83 CX_TEST_ASSERT(buf.size == 8); |
|
84 CX_TEST_ASSERT(buf.pos == 8); |
|
85 CX_TEST_ASSERT(buf.capacity == 8); |
|
86 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0); |
|
87 } |
|
88 |
|
89 TEST_F(BufferWrite, SizeOneDiscard) { |
|
90 const char *data = "testing"; |
|
91 CX_TEST_ASSERT(buf.capacity == 8); |
|
92 CX_TEST_ASSERT(buf.pos == 4); |
|
93 CX_TEST_ASSERT(buf.size == 4); |
|
94 size_t written = cxBufferWrite(data, 1, 7, &buf); |
|
95 CX_TEST_ASSERT(written == 4); |
|
96 CX_TEST_ASSERT(buf.size == 8); |
|
97 CX_TEST_ASSERT(buf.pos == 8); |
|
98 CX_TEST_ASSERT(buf.capacity == 8); |
|
99 EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0); |
|
100 } |
|
101 |
|
102 TEST_F(BufferWrite, SizeOneExtend) { |
|
103 buf.flags |= CX_BUFFER_AUTO_EXTEND; |
|
104 const char *data = "testing"; |
|
105 CX_TEST_ASSERT(buf.capacity == 8); |
|
106 CX_TEST_ASSERT(buf.pos == 4); |
|
107 CX_TEST_ASSERT(buf.size == 4); |
|
108 size_t written = cxBufferWrite(data, 1, 7, &buf); |
|
109 CX_TEST_ASSERT(written == 7); |
|
110 CX_TEST_ASSERT(buf.size == 11); |
|
111 CX_TEST_ASSERT(buf.pos == 11); |
|
112 EXPECT_GE(buf.capacity, 11); |
|
113 EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0); |
|
114 } |
|
115 |
|
116 TEST_F(BufferWrite, MultibyteFit) { |
|
117 const char *data = "test"; |
|
118 CX_TEST_ASSERT(buf.capacity == 8); |
|
119 CX_TEST_ASSERT(buf.pos == 4); |
|
120 CX_TEST_ASSERT(buf.size == 4); |
|
121 size_t written = cxBufferWrite(data, 2, 2, &buf); |
|
122 CX_TEST_ASSERT(written == 2); |
|
123 CX_TEST_ASSERT(buf.size == 8); |
|
124 CX_TEST_ASSERT(buf.pos == 8); |
|
125 CX_TEST_ASSERT(buf.capacity == 8); |
|
126 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0); |
|
127 } |
|
128 |
|
129 TEST_F(BufferWrite, MultibyteDiscard) { |
|
130 const char *data = "testing"; |
|
131 CX_TEST_ASSERT(buf.capacity == 8); |
|
132 CX_TEST_ASSERT(buf.size == 4); |
|
133 buf.pos = 3; |
|
134 size_t written = cxBufferWrite(data, 2, 4, &buf); |
|
135 // remember: whole elements are discarded if they do not fit |
|
136 CX_TEST_ASSERT(written == 2); |
|
137 CX_TEST_ASSERT(buf.size == 7); |
|
138 CX_TEST_ASSERT(buf.pos == 7); |
|
139 CX_TEST_ASSERT(buf.capacity == 8); |
|
140 EXPECT_EQ(memcmp(buf.space, "pretest\0", 8), 0); |
|
141 } |
|
142 |
|
143 TEST_F(BufferWrite, MultibyteExtend) { |
|
144 buf.flags |= CX_BUFFER_AUTO_EXTEND; |
|
145 const char *data = "tester"; |
|
146 CX_TEST_ASSERT(buf.capacity == 8); |
|
147 CX_TEST_ASSERT(buf.size == 4); |
|
148 buf.pos = 3; |
|
149 size_t written = cxBufferWrite(data, 2, 3, &buf); |
|
150 // remember: whole elements are discarded if they do not fit |
|
151 CX_TEST_ASSERT(written == 3); |
|
152 CX_TEST_ASSERT(buf.size == 9); |
|
153 CX_TEST_ASSERT(buf.pos == 9); |
|
154 EXPECT_GE(buf.capacity, 9); |
|
155 EXPECT_EQ(memcmp(buf.space, "pretester", 9), 0); |
|
156 } |
|
157 |
|
158 TEST_F(BufferWrite, PutcWrapperFit) { |
|
159 CX_TEST_ASSERT(buf.capacity == 8); |
|
160 CX_TEST_ASSERT(buf.pos == 4); |
|
161 CX_TEST_ASSERT(buf.size == 4); |
|
162 int c = cxBufferPut(&buf, 0x200 | 'a'); |
|
163 CX_TEST_ASSERT(c == 'a'); |
|
164 CX_TEST_ASSERT(buf.size == 5); |
|
165 CX_TEST_ASSERT(buf.pos == 5); |
|
166 CX_TEST_ASSERT(buf.capacity == 8); |
|
167 EXPECT_EQ(memcmp(buf.space, "prepa\0", 6), 0); |
|
168 } |
|
169 |
|
170 TEST_F(BufferWrite, PutcWrapperDiscard) { |
|
171 CX_TEST_ASSERT(buf.capacity == 8); |
|
172 CX_TEST_ASSERT(buf.size == 4); |
|
173 buf.pos = 8; |
|
174 int c = cxBufferPut(&buf, 0x200 | 'a'); |
|
175 CX_TEST_ASSERT(c == EOF); |
|
176 CX_TEST_ASSERT(buf.size == 4); |
|
177 CX_TEST_ASSERT(buf.pos == 8); |
|
178 CX_TEST_ASSERT(buf.capacity == 8); |
|
179 EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0\0", 9), 0); |
|
180 } |
|
181 |
|
182 TEST_F(BufferWrite, PutcWrapperExtend) { |
|
183 buf.flags |= CX_BUFFER_AUTO_EXTEND; |
|
184 CX_TEST_ASSERT(buf.capacity == 8); |
|
185 CX_TEST_ASSERT(buf.size == 4); |
|
186 buf.pos = 8; |
|
187 int c = cxBufferPut(&buf, 0x200 | 'a'); |
|
188 CX_TEST_ASSERT(c == 'a'); |
|
189 CX_TEST_ASSERT(buf.size == 9); |
|
190 CX_TEST_ASSERT(buf.pos == 9); |
|
191 EXPECT_GE(buf.capacity, 9); |
|
192 EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0a", 9), 0); |
|
193 } |
|
194 |
|
195 TEST_F(BufferWrite, PutStringWrapperFit) { |
|
196 const char *data = "test"; |
|
197 CX_TEST_ASSERT(buf.capacity == 8); |
|
198 CX_TEST_ASSERT(buf.pos == 4); |
|
199 CX_TEST_ASSERT(buf.size == 4); |
|
200 size_t written = cxBufferPutString(&buf, data); |
|
201 CX_TEST_ASSERT(written == 4); |
|
202 CX_TEST_ASSERT(buf.size == 8); |
|
203 CX_TEST_ASSERT(buf.pos == 8); |
|
204 CX_TEST_ASSERT(buf.capacity == 8); |
|
205 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0); |
|
206 } |
|
207 |
|
208 TEST_F(BufferWrite, PutStringWrapperDiscard) { |
|
209 const char *data = "testing"; |
|
210 CX_TEST_ASSERT(buf.capacity == 8); |
|
211 CX_TEST_ASSERT(buf.pos == 4); |
|
212 CX_TEST_ASSERT(buf.size == 4); |
|
213 size_t written = cxBufferPutString(&buf, data); |
|
214 CX_TEST_ASSERT(written == 4); |
|
215 CX_TEST_ASSERT(buf.size == 8); |
|
216 CX_TEST_ASSERT(buf.pos == 8); |
|
217 CX_TEST_ASSERT(buf.capacity == 8); |
|
218 EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0); |
|
219 } |
|
220 |
|
221 TEST_F(BufferWrite, PutStringWrapperExtend) { |
|
222 buf.flags |= CX_BUFFER_AUTO_EXTEND; |
|
223 const char *data = "testing"; |
|
224 CX_TEST_ASSERT(buf.capacity == 8); |
|
225 CX_TEST_ASSERT(buf.pos == 4); |
|
226 CX_TEST_ASSERT(buf.size == 4); |
|
227 size_t written = cxBufferPutString(&buf, data); |
|
228 CX_TEST_ASSERT(written == 7); |
|
229 CX_TEST_ASSERT(buf.size == 11); |
|
230 CX_TEST_ASSERT(buf.pos == 11); |
|
231 EXPECT_GE(buf.capacity, 11); |
|
232 EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0); |
|
233 } |
|
234 |
|
235 TEST_F(BufferWrite, MultOverflow) { |
|
236 const char *data = "testing"; |
|
237 CX_TEST_ASSERT(buf.capacity == 8); |
|
238 CX_TEST_ASSERT(buf.pos == 4); |
|
239 CX_TEST_ASSERT(buf.size == 4); |
|
240 size_t written = cxBufferWrite(data, 8, SIZE_MAX / 4, &buf); |
|
241 CX_TEST_ASSERT(written == 0); |
|
242 CX_TEST_ASSERT(buf.capacity == 8); |
|
243 CX_TEST_ASSERT(buf.pos == 4); |
|
244 CX_TEST_ASSERT(buf.size == 4); |
|
245 EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0); |
|
246 } |
|
247 |
|
248 TEST_F(BufferWrite, MaxCapaOverflow) { |
|
249 buf.flags |= CX_BUFFER_AUTO_EXTEND; |
|
250 const char *data = "testing"; |
|
251 CX_TEST_ASSERT(buf.capacity == 8); |
|
252 CX_TEST_ASSERT(buf.pos == 4); |
|
253 CX_TEST_ASSERT(buf.size == 4); |
|
254 size_t written = cxBufferWrite(data, 1, SIZE_MAX - 2, &buf); |
|
255 CX_TEST_ASSERT(written == 0); |
|
256 CX_TEST_ASSERT(buf.capacity == 8); |
|
257 CX_TEST_ASSERT(buf.pos == 4); |
|
258 CX_TEST_ASSERT(buf.size == 4); |
|
259 EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0); |
|
260 } |
|
261 |
|
262 TEST_F(BufferWrite, OnlyOverwrite) { |
|
263 buf.flags |= CX_BUFFER_AUTO_EXTEND; |
|
264 CX_TEST_ASSERT(buf.capacity == 8); |
|
265 memcpy(buf.space, "preptest", 8); |
|
266 buf.pos = 3; |
|
267 buf.size = 8; |
|
268 size_t written = cxBufferWrite("XXX", 2, 2, &buf); |
|
269 CX_TEST_ASSERT(written == 2); |
|
270 CX_TEST_ASSERT(buf.capacity == 8); |
|
271 CX_TEST_ASSERT(buf.size == 8); |
|
272 CX_TEST_ASSERT(buf.pos == 7); |
|
273 EXPECT_EQ(memcmp(buf.space, "preXXX\0t", 8), 0); |
|
274 } |
|
275 |
|
276 TEST_F(BufferWrite, FlushAtCapacity) { |
|
277 enableFlushing(); |
|
278 CX_TEST_ASSERT(buf.capacity == 8); |
|
279 CX_TEST_ASSERT(buf.pos == 4); |
|
280 size_t written = cxBufferWrite("foo", 1, 3, &buf); |
|
281 CX_TEST_ASSERT(written == 3); |
|
282 CX_TEST_ASSERT(buf.pos == 7); |
|
283 CX_TEST_ASSERT(buf.size == 7); |
|
284 CX_TEST_ASSERT(target.pos == 0); |
|
285 CX_TEST_ASSERT(target.size == 0); |
|
286 written = cxBufferWrite("hello", 1, 5, &buf); |
|
287 CX_TEST_ASSERT(written == 5); |
|
288 CX_TEST_ASSERT(buf.pos == 0); |
|
289 CX_TEST_ASSERT(buf.size == 0); |
|
290 CX_TEST_ASSERT(buf.capacity == 8); |
|
291 CX_TEST_ASSERT(target.pos == 12); |
|
292 CX_TEST_ASSERT(target.size == 12); |
|
293 EXPECT_EQ(memcmp(target.space, "prepfoohello", 12), 0); |
|
294 } |
|
295 |
|
296 TEST_F(BufferWrite, FlushAtThreshold) { |
|
297 enableFlushing(); |
|
298 buf.flush_threshold = 12; |
|
299 buf.flags |= CX_BUFFER_AUTO_EXTEND; |
|
300 CX_TEST_ASSERT(buf.capacity == 8); |
|
301 CX_TEST_ASSERT(buf.pos == 4); |
|
302 size_t written = cxBufferWrite("foobar", 1, 6, &buf); |
|
303 CX_TEST_ASSERT(written == 6); |
|
304 CX_TEST_ASSERT(buf.pos == 10); |
|
305 CX_TEST_ASSERT(buf.size == 10); |
|
306 ASSERT_GE(buf.capacity, 10); |
|
307 ASSERT_LE(buf.capacity, 12); |
|
308 CX_TEST_ASSERT(target.pos == 0); |
|
309 CX_TEST_ASSERT(target.size == 0); |
|
310 written = cxBufferWrite("hello", 1, 5, &buf); |
|
311 CX_TEST_ASSERT(written == 5); |
|
312 CX_TEST_ASSERT(buf.pos == 0); |
|
313 CX_TEST_ASSERT(buf.size == 0); |
|
314 EXPECT_LE(buf.capacity, 12); |
|
315 CX_TEST_ASSERT(target.pos == 15); |
|
316 CX_TEST_ASSERT(target.size == 15); |
|
317 EXPECT_EQ(memcmp(target.space, "prepfoobarhello", 15), 0); |
|
318 } |
|
319 |
|
320 TEST_F(BufferWrite, FlushRateLimited) { |
|
321 enableFlushing(); |
|
322 // limit the rate of the flush function and the capacity of the target |
|
323 target.capacity = 16; |
|
324 target.flags &= ~CX_BUFFER_AUTO_EXTEND; |
|
325 buf.flush_func = (cx_write_func) mock_write_limited_rate; |
|
326 CX_TEST_ASSERT(buf.capacity == 8); |
|
327 CX_TEST_ASSERT(buf.pos == 4); |
|
328 size_t written = cxBufferWrite("foo", 1, 3, &buf); |
|
329 CX_TEST_ASSERT(written == 3); |
|
330 CX_TEST_ASSERT(buf.pos == 7); |
|
331 CX_TEST_ASSERT(buf.size == 7); |
|
332 CX_TEST_ASSERT(target.pos == 0); |
|
333 CX_TEST_ASSERT(target.size == 0); |
|
334 written = cxBufferWrite("hello, world!", 1, 13, &buf); |
|
335 // " world!" fits into this buffer, the remaining stuff is flushed out |
|
336 CX_TEST_ASSERT(written == 13); |
|
337 CX_TEST_ASSERT(buf.pos == 7); |
|
338 CX_TEST_ASSERT(buf.size == 7); |
|
339 CX_TEST_ASSERT(buf.capacity == 8); |
|
340 EXPECT_EQ(memcmp(buf.space, " world!", 7), 0); |
|
341 CX_TEST_ASSERT(target.pos == 13); |
|
342 CX_TEST_ASSERT(target.size == 13); |
|
343 CX_TEST_ASSERT(target.capacity == 16); |
|
344 EXPECT_EQ(memcmp(target.space, "prepfoohello,", 13), 0); |
|
345 } |
|
346 |
|
347 class BufferRead : public ::testing::Test { |
|
348 protected: |
|
349 CxBuffer buf{}; |
|
350 |
|
351 void SetUp() override { |
|
352 cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); |
|
353 buf.capacity = 8; // artificially reduce capacity to check OOB writes |
|
354 memset(buf.space, 0, 16); |
|
355 memcpy(buf.space, "some data", 9); |
|
356 buf.size = 9; |
|
357 } |
|
358 |
|
359 void TearDown() override { |
|
360 cxBufferDestroy(&buf); |
|
361 } |
|
362 }; |
|
363 |
|
364 TEST_F(BufferRead, GetByte) { |
|
365 buf.pos = 2; |
|
366 EXPECT_EQ(cxBufferGet(&buf), 'm'); |
|
367 EXPECT_EQ(cxBufferGet(&buf), 'e'); |
|
368 EXPECT_EQ(cxBufferGet(&buf), ' '); |
|
369 EXPECT_EQ(cxBufferGet(&buf), 'd'); |
|
370 CX_TEST_ASSERT(buf.pos == 6); |
|
371 } |
|
372 |
|
373 TEST_F(BufferRead, GetEof) { |
|
374 buf.pos = buf.size; |
|
375 EXPECT_EQ(cxBufferGet(&buf), EOF); |
|
376 } |
|
377 |
|
378 TEST_F(BufferRead, ReadWithinBounds) { |
|
379 buf.pos = 2; |
|
380 char target[4]; |
|
381 auto read = cxBufferRead(&target, 1, 4, &buf); |
|
382 CX_TEST_ASSERT(read == 4); |
|
383 EXPECT_EQ(memcmp(&target, "me d", 4), 0); |
|
384 CX_TEST_ASSERT(buf.pos == 6); |
|
385 } |
|
386 |
|
387 TEST_F(BufferRead, ReadOutOfBounds) { |
|
388 buf.pos = 6; |
|
389 char target[4]; |
|
390 auto read = cxBufferRead(&target, 1, 4, &buf); |
|
391 CX_TEST_ASSERT(read == 3); |
|
392 EXPECT_EQ(memcmp(&target, "ata", 3), 0); |
|
393 CX_TEST_ASSERT(buf.pos == 9); |
|
394 } |
|
395 |
|
396 TEST_F(BufferRead, ReadOutOfBoundsMultibyte) { |
|
397 buf.pos = 6; |
|
398 char target[4]; |
|
399 target[2] = '\0'; |
|
400 auto read = cxBufferRead(&target, 2, 2, &buf); |
|
401 CX_TEST_ASSERT(read == 1); |
|
402 EXPECT_EQ(memcmp(&target, "at\0", 3), 0); |
|
403 CX_TEST_ASSERT(buf.pos == 8); |
|
404 } |
|
405 |
|
406 TEST_F(BufferRead, ReadEof) { |
|
407 buf.pos = 9; |
|
408 char target[4]; |
|
409 auto read = cxBufferRead(&target, 1, 1, &buf); |
|
410 CX_TEST_ASSERT(read == 0); |
|
411 CX_TEST_ASSERT(buf.pos == 9); |
|
412 } |
|