Branch data Line data Source code
1 : : #include "test_libmem_all.h"
2 : :
3 : : /**
4 : : * @brief Run README example 01 that intentionally shows a frame error
5 : : *
6 : : * This example creates a default data descriptor and then calls a string
7 : : * helper on it. The library must reject that programmer-side frame mistake
8 : : * with FAILURE and a clear diagnostic, while the descriptor remains safe to
9 : : * delete afterwards
10 : : *
11 : : * @return Return describing success or failure
12 : : */
13 : 1 : static Return test_libmem_0000_01_body(void)
14 : : {
15 : 1 : INITTEST;
16 : :
17 : 1 : m_create(char,destinations_string);
18 : :
19 : 1 : const char *source_string = "Hello world";
20 : :
21 : 1 : ASSERT(FAILURE == m_copy_string(destinations_string,source_string));
22 : 1 : call(m_del(destinations_string));
23 : :
24 : 1 : deliver(status);
25 : : }
26 : :
27 : : /**
28 : : * @brief Check README example 01 diagnostic capture
29 : : *
30 : : * @return Return describing success or failure
31 : : */
32 : 1 : static Return test_libmem_0000_01(void)
33 : : {
34 : 1 : INITTEST;
35 : :
36 : : static const char expected_stderr_pattern_libmem_0000_01[] =
37 : : "\\A"
38 : : "ERROR: src/mem_core_string\\.c:mem_core_string:\\d+ Memory management; Destination must be a string descriptor Errno: [^\\n]+ \\(errno: [0-9]+\\)\n"
39 : : "\\Z";
40 : :
41 : 1 : ASSERT(SUCCESS == match_function_output(NULL,expected_stderr_pattern_libmem_0000_01,test_libmem_0000_01_body));
42 : :
43 : 1 : RETURN_STATUS;
44 : : }
45 : :
46 : : /**
47 : : * @brief Check README example 02 with a descriptor created as MEMORY_STRING
48 : : *
49 : : * @return Return describing success or failure
50 : : */
51 : 1 : static Return test_libmem_0000_02(void)
52 : : {
53 : 1 : INITTEST;
54 : :
55 : 1 : m_create(char,destinations_string,MEMORY_STRING);
56 : :
57 : 1 : const char *source_string = "Hello world";
58 : :
59 : 1 : ASSERT(SUCCESS == m_copy_string(destinations_string,source_string));
60 : 1 : ASSERT(destinations_string->is_string == true);
61 : 1 : ASSERT(destinations_string->string_length == strlen(source_string));
62 : 1 : ASSERT(destinations_string->length == strlen(source_string) + 1U);
63 : 1 : ASSERT(strcmp(m_text(destinations_string),source_string) == 0);
64 : :
65 : 1 : call(m_del(destinations_string));
66 : :
67 : 1 : RETURN_STATUS;
68 : : }
69 : :
70 : : /**
71 : : * @brief Check README example 03 with explicit conversion to string mode
72 : : *
73 : : * @return Return describing success or failure
74 : : */
75 : 1 : static Return test_libmem_0000_03(void)
76 : : {
77 : 1 : INITTEST;
78 : :
79 : 1 : m_create(char,destinations_string);
80 : :
81 : 1 : const char *source_string = "Hello world";
82 : :
83 : 1 : ASSERT(destinations_string->is_string == false);
84 : 1 : ASSERT(SUCCESS == m_to_string(destinations_string));
85 : 1 : ASSERT(destinations_string->is_string == true);
86 : 1 : ASSERT(SUCCESS == m_copy_string(destinations_string,source_string));
87 : 1 : ASSERT(destinations_string->string_length == strlen(source_string));
88 : 1 : ASSERT(destinations_string->length == strlen(source_string) + 1U);
89 : 1 : ASSERT(strcmp(m_text(destinations_string),source_string) == 0);
90 : :
91 : 1 : call(m_del(destinations_string));
92 : :
93 : 1 : RETURN_STATUS;
94 : : }
95 : :
96 : : /**
97 : : * @brief Check README example 04 with typed data and self-aliased append
98 : : *
99 : : * Builds a typed point descriptor whose initial payload fits exactly in one
100 : : * allocation block, copies it to the destination, and then appends the
101 : : * destination to itself. The append must grow the destination reserve while
102 : : * preserving both copies of every original point, proving that a source view
103 : : * inside storage being resized remains valid for the operation
104 : : *
105 : : * @return Return describing success or failure
106 : : */
107 : 1 : static Return test_libmem_0000_04(void)
108 : : {
109 : 1 : INITTEST;
110 : :
111 : 1 : m_create(point,points);
112 : 1 : m_create(point,mirror);
113 : :
114 : : /* Fill one reserve block so self-append must grow the destination storage */
115 : 1 : const size_t original_point_count = MEMORY_BLOCK_BYTES / sizeof(point);
116 : 1 : ASSERT(original_point_count > 0U);
117 : 1 : ASSERT(SUCCESS == m_resize(points,original_point_count));
118 : :
119 : 1 : point *p = m_data(point,points);
120 : 1 : ASSERT(p != NULL);
121 : :
122 : 1 : IF(p != NULL)
123 : : {
124 [ + + ]: 513 : for(size_t i = 0; i < points->length; ++i)
125 : : {
126 : 512 : p[i] = (point){(int)i,(int)i};
127 : : }
128 : : }
129 : :
130 : : /* Append mirror to itself while its source payload is owned by the buffer being enlarged */
131 : 1 : ASSERT(SUCCESS == m_copy(mirror,points));
132 : 1 : const size_t allocated_before_append = mirror->actually_allocated_bytes;
133 : 1 : ASSERT(allocated_before_append == MEMORY_BLOCK_BYTES);
134 : 1 : ASSERT(SUCCESS == m_concat_data(mirror,mirror));
135 : 1 : ASSERT(mirror->is_string == false);
136 : 1 : ASSERT(mirror->single_element_size == sizeof(point));
137 : 1 : ASSERT(mirror->length == original_point_count * 2U);
138 : 1 : ASSERT(mirror->actually_allocated_bytes > allocated_before_append);
139 : :
140 : 1 : const point *view = m_data_ro(point,mirror);
141 : 1 : ASSERT(view != NULL);
142 : :
143 : 1 : IF(view != NULL)
144 : : {
145 [ + + ]: 513 : for(size_t i = 0; i < original_point_count; ++i)
146 : : {
147 : 512 : ASSERT(view[i].x == (int)i);
148 : 512 : ASSERT(view[i].y == (int)i);
149 : 512 : ASSERT(view[i + original_point_count].x == (int)i);
150 : 512 : ASSERT(view[i + original_point_count].y == (int)i);
151 : : }
152 : : }
153 : :
154 : 1 : call(m_del(points));
155 : 1 : call(m_del(mirror));
156 : :
157 : 1 : RETURN_STATUS;
158 : : }
159 : :
160 : : /**
161 : : * @brief Check README example 05 converting data to text within existing reserve
162 : : *
163 : : * Builds a log prefix as raw byte data in a block that already has room for a
164 : : * trailing zero terminator. Conversion to string mode must preserve the same
165 : : * physical allocation while expanding the logical payload by one terminator
166 : : * element and enabling later string concatenation
167 : : *
168 : : * @return Return describing success or failure
169 : : */
170 : 1 : static Return test_libmem_0000_05(void)
171 : : {
172 : 1 : INITTEST;
173 : :
174 : 1 : m_create(char,log,MEMORY_DATA);
175 : :
176 : : /* Phase one stores raw bytes without string-mode terminator semantics */
177 : 1 : ASSERT(SUCCESS == m_copy_buffer(log,5,"GET /"));
178 : 1 : ASSERT(SUCCESS == m_concat_buffer(log,4,"api "));
179 : 1 : ASSERT(log->is_string == false);
180 : 1 : ASSERT(log->length == 9U);
181 : 1 : ASSERT(log->data != NULL);
182 : 1 : ASSERT(log->actually_allocated_bytes > log->length);
183 : :
184 : 1 : const void *const data_before_conversion = log->data;
185 : 1 : const size_t allocated_before_conversion = log->actually_allocated_bytes;
186 : :
187 : : /* Spare reserve lets conversion add a terminator without replacing the physical buffer */
188 : 1 : ASSERT(SUCCESS == m_to_string(log));
189 : 1 : ASSERT(log->data == data_before_conversion);
190 : 1 : ASSERT(log->actually_allocated_bytes == allocated_before_conversion);
191 : 1 : ASSERT(log->is_string == true);
192 : 1 : ASSERT(log->string_length == strlen("GET /api "));
193 : 1 : ASSERT(log->length == strlen("GET /api ") + 1U);
194 : 1 : ASSERT(strcmp(m_text(log),"GET /api ") == 0);
195 : :
196 : 1 : ASSERT(SUCCESS == m_concat_literal(log,"200 OK"));
197 : 1 : ASSERT(log->string_length == strlen("GET /api 200 OK"));
198 : 1 : ASSERT(log->length == strlen("GET /api 200 OK") + 1U);
199 : 1 : ASSERT(strcmp(m_text(log),"GET /api 200 OK") == 0);
200 : :
201 : 1 : call(m_del(log));
202 : :
203 : 1 : RETURN_STATUS;
204 : : }
205 : :
206 : : /**
207 : : * @brief Check README example 06 reusing a descriptor after buffer release
208 : : *
209 : : * Stores a string, releases its descriptor-owned buffer with @ref m_del, and
210 : : * verifies that the descriptor is reset while preserving string mode. A later
211 : : * string copy through the same descriptor must allocate usable storage again
212 : : *
213 : : * @return Return describing success or failure
214 : : */
215 : 1 : static Return test_libmem_0000_06(void)
216 : : {
217 : 1 : INITTEST;
218 : :
219 : 1 : m_create(char,greeting,MEMORY_STRING);
220 : :
221 : 1 : ASSERT(SUCCESS == m_copy_literal(greeting,"alive"));
222 : 1 : ASSERT(strcmp(m_text(greeting),"alive") == 0);
223 : :
224 : : /* Release owned storage while retaining an initialized string descriptor */
225 : 1 : call(m_del(greeting));
226 : 1 : ASSERT(greeting->data == NULL);
227 : 1 : ASSERT(greeting->length == 0U);
228 : 1 : ASSERT(greeting->string_length == 0U);
229 : 1 : ASSERT(greeting->is_string == true);
230 : :
231 : : /* Reuse the cleared descriptor through the API, not any pointer to its former buffer */
232 : 1 : ASSERT(SUCCESS == m_copy_literal(greeting,"reborn"));
233 : 1 : ASSERT(greeting->is_string == true);
234 : 1 : ASSERT(greeting->string_length == strlen("reborn"));
235 : 1 : ASSERT(strcmp(m_text(greeting),"reborn") == 0);
236 : :
237 : 1 : call(m_del(greeting));
238 : :
239 : 1 : RETURN_STATUS;
240 : : }
241 : :
242 : : /**
243 : : * @brief Check README example 07 reusing an imported string terminator
244 : : *
245 : : * Copies the complete terminated byte representation of `"abc"` into a data
246 : : * descriptor. Conversion to string mode must recognize the imported zero
247 : : * terminator, cache the visible length, and leave the logical element count
248 : : * unchanged
249 : : *
250 : : * @return Return describing success or failure
251 : : */
252 : 1 : static Return test_libmem_0000_07(void)
253 : : {
254 : 1 : INITTEST;
255 : :
256 : 1 : m_create(char,buffer);
257 : :
258 : : /* Copy raw payload together with its trailing zero element in data mode */
259 : 1 : ASSERT(SUCCESS == m_copy_buffer(buffer,sizeof("abc"),"abc"));
260 : 1 : ASSERT(buffer->is_string == false);
261 : 1 : ASSERT(buffer->length == sizeof("abc"));
262 : :
263 : : /* Conversion recognizes the existing terminator instead of adding an element */
264 : 1 : ASSERT(SUCCESS == m_to_string(buffer));
265 : 1 : ASSERT(buffer->is_string == true);
266 : 1 : ASSERT(buffer->string_length == 3U);
267 : 1 : ASSERT(buffer->length == sizeof("abc"));
268 : 1 : ASSERT(strcmp(m_text(buffer),"abc") == 0);
269 : :
270 : 1 : call(m_del(buffer));
271 : :
272 : 1 : RETURN_STATUS;
273 : : }
274 : :
275 : : /**
276 : : * @brief Check README example 08 literal copy expands to fixed-string semantics
277 : : *
278 : : * Copies a C string literal through the convenience macro and verifies that
279 : : * it stores the same visible length and terminator-inclusive logical length
280 : : * as an explicit fixed-string copy of the same literal
281 : : *
282 : : * @return Return describing success or failure
283 : : */
284 : 1 : static Return test_libmem_0000_08(void)
285 : : {
286 : 1 : INITTEST;
287 : :
288 : 1 : m_create(char,title,MEMORY_STRING);
289 : :
290 : : /* The literal wrapper must derive the complete terminated array size */
291 : 1 : ASSERT(SUCCESS == m_copy_literal(title,"hello"));
292 : 1 : ASSERT(title->string_length == strlen("hello"));
293 : 1 : ASSERT(title->length == sizeof("hello"));
294 : 1 : ASSERT(strcmp(m_text(title),"hello") == 0);
295 : :
296 : : /* Spelling out sizeof(...) explicitly must produce the same string state */
297 : 1 : ASSERT(SUCCESS == m_copy_fixed_string(title,sizeof("hello"),"hello"));
298 : 1 : ASSERT(title->string_length == strlen("hello"));
299 : 1 : ASSERT(title->length == sizeof("hello"));
300 : 1 : ASSERT(strcmp(m_text(title),"hello") == 0);
301 : :
302 : 1 : call(m_del(title));
303 : :
304 : 1 : RETURN_STATUS;
305 : : }
306 : :
307 : : /**
308 : : * @brief Check README example 09 for formatted string construction
309 : : *
310 : : * Formats an unsigned value into a file-name pattern using a byte-sized string
311 : : * descriptor. The rendered result must remain in string mode, cache its
312 : : * visible length, and account for one final terminator in its logical length
313 : : *
314 : : * @return Return describing success or failure
315 : : */
316 : 1 : static Return test_libmem_0000_09(void)
317 : : {
318 : 1 : INITTEST;
319 : :
320 : 1 : m_create(char,title,MEMORY_STRING);
321 : :
322 : 1 : ASSERT(SUCCESS == m_formatted_string(title,"file-%u.txt",7U));
323 : 1 : ASSERT(title->is_string == true);
324 : 1 : ASSERT(title->string_length == strlen("file-7.txt"));
325 : 1 : ASSERT(title->length == strlen("file-7.txt") + 1U);
326 : 1 : ASSERT(strcmp(m_text(title),"file-7.txt") == 0);
327 : :
328 : 1 : call(m_del(title));
329 : :
330 : 1 : RETURN_STATUS;
331 : : }
332 : :
333 : : /**
334 : : * @brief Check README example 10 that finalizes a shortened direct write
335 : : *
336 : : * Starts with a complete string descriptor, overwrites only a shorter visible
337 : : * prefix through its writable buffer, and finalizes that prefix as `"ALPHA"`.
338 : : * Finalization must update the cached string length and terminator without
339 : : * reducing the descriptor's existing logical length
340 : : *
341 : : * @return Return describing success or failure
342 : : */
343 : 1 : static Return test_libmem_0000_10(void)
344 : : {
345 : 1 : INITTEST;
346 : :
347 : 1 : m_create(char,buffer,MEMORY_STRING);
348 : :
349 : : /* Prepare a longer string whose existing storage will be rewritten in place */
350 : 1 : ASSERT(SUCCESS == m_copy_literal(buffer,"alphabet"));
351 : 1 : ASSERT(buffer->length == sizeof("alphabet"));
352 : :
353 : 1 : char *writable = m_data(char,buffer);
354 : 1 : ASSERT(writable != NULL);
355 : :
356 : : /* Directly replace only the visible prefix while leaving its old tail present */
357 : 1 : IF(writable != NULL)
358 : : {
359 : 1 : writable[0] = 'A';
360 : 1 : writable[1] = 'L';
361 : 1 : writable[2] = 'P';
362 : 1 : writable[3] = 'H';
363 : 1 : writable[4] = 'A';
364 : : }
365 : :
366 : : /* Finalization makes only the rewritten prefix visible and leaves reserve available */
367 : 1 : ASSERT(SUCCESS == m_finalize_string(buffer,5));
368 : 1 : ASSERT(buffer->is_string == true);
369 : 1 : ASSERT(buffer->string_length == 5U);
370 : 1 : ASSERT(buffer->length == sizeof("alphabet"));
371 : 1 : ASSERT(strcmp(m_text(buffer),"ALPHA") == 0);
372 : :
373 : 1 : call(m_del(buffer));
374 : :
375 : 1 : RETURN_STATUS;
376 : : }
377 : :
378 : : /**
379 : : * @brief Check README example 11 for explicit fixed-string copying
380 : : *
381 : : * Copies a named fixed-size character array whose final element is guaranteed
382 : : * to be the zero terminator. This demonstrates the fixed-string contract:
383 : : * m_copy_fixed_string(...) accepts a trusted complete source size and produces
384 : : * a string descriptor with the matching visible and terminator-inclusive lengths
385 : : *
386 : : * @return Return describing success or failure
387 : : */
388 : 1 : static Return test_libmem_0000_11(void)
389 : : {
390 : 1 : INITTEST;
391 : :
392 : 1 : m_create(char,dest,MEMORY_STRING);
393 : :
394 : 1 : const char text[] = {'H','e','l','l','o','\0'};
395 : :
396 : 1 : ASSERT(SUCCESS == m_copy_fixed_string(dest,sizeof(text),text));
397 : 1 : ASSERT(dest->is_string == true);
398 : 1 : ASSERT(dest->string_length == strlen("Hello"));
399 : 1 : ASSERT(dest->length == sizeof(text));
400 : 1 : ASSERT(strcmp(m_text(dest),"Hello") == 0);
401 : :
402 : 1 : call(m_del(dest));
403 : :
404 : 1 : RETURN_STATUS;
405 : : }
406 : :
407 : : /**
408 : : * @brief Check README example 12 for explicit fixed-string append
409 : : *
410 : : * Starts with an existing string and appends a named fixed-size suffix whose
411 : : * final element is guaranteed to be the zero terminator. This demonstrates
412 : : * that m_concat_fixed_string(...) extends the visible text while trusting the
413 : : * supplied complete source size instead of scanning for a terminator
414 : : *
415 : : * @return Return describing success or failure
416 : : */
417 : 1 : static Return test_libmem_0000_12(void)
418 : : {
419 : 1 : INITTEST;
420 : :
421 : 1 : m_create(char,dest,MEMORY_STRING);
422 : :
423 : 1 : const char suffix[] = {'-','e','n','d','\0'};
424 : :
425 : 1 : ASSERT(SUCCESS == m_copy_literal(dest,"start"));
426 : 1 : ASSERT(SUCCESS == m_concat_fixed_string(dest,sizeof(suffix),suffix));
427 : 1 : ASSERT(dest->is_string == true);
428 : 1 : ASSERT(dest->string_length == strlen("start-end"));
429 : 1 : ASSERT(dest->length == strlen("start-end") + 1U);
430 : 1 : ASSERT(strcmp(m_text(dest),"start-end") == 0);
431 : :
432 : 1 : call(m_del(dest));
433 : :
434 : 1 : RETURN_STATUS;
435 : : }
436 : :
437 : : /**
438 : : * @brief Check README example 13 for literal append and fixed-string equivalence
439 : : *
440 : : * Appends the same literal first through the convenience macro and then through
441 : : * the explicit fixed-string helper. Both paths must produce matching visible
442 : : * text and terminator-inclusive descriptor lengths because the macro supplies
443 : : * the complete literal size automatically
444 : : *
445 : : * @return Return describing success or failure
446 : : */
447 : 1 : static Return test_libmem_0000_13(void)
448 : : {
449 : 1 : INITTEST;
450 : :
451 : 1 : m_create(char,dest,MEMORY_STRING);
452 : :
453 : 1 : ASSERT(SUCCESS == m_copy_literal(dest,"base"));
454 : 1 : ASSERT(SUCCESS == m_concat_literal(dest,"-suffix"));
455 : 1 : ASSERT(dest->string_length == strlen("base-suffix"));
456 : 1 : ASSERT(dest->length == strlen("base-suffix") + 1U);
457 : 1 : ASSERT(strcmp(m_text(dest),"base-suffix") == 0);
458 : :
459 : 1 : ASSERT(SUCCESS == m_copy_literal(dest,"base"));
460 : 1 : ASSERT(SUCCESS == m_concat_fixed_string(dest,sizeof("-suffix"),"-suffix"));
461 : 1 : ASSERT(dest->string_length == strlen("base-suffix"));
462 : 1 : ASSERT(dest->length == strlen("base-suffix") + 1U);
463 : 1 : ASSERT(strcmp(m_text(dest),"base-suffix") == 0);
464 : :
465 : 1 : call(m_del(dest));
466 : :
467 : 1 : RETURN_STATUS;
468 : : }
469 : :
470 : : /**
471 : : * @brief Run README example 14 for finalized writes and descriptor concatenation
472 : : *
473 : : * Creates two string descriptors with reserved writable capacity, fills their
474 : : * visible payloads through raw pointers, and finalizes their cached lengths
475 : : * before appending one descriptor to the other and printing the concatenated
476 : : * result for output validation
477 : : *
478 : : * @return Return describing success or failure
479 : : */
480 : 1 : static Return test_libmem_0000_14_body(void)
481 : : {
482 : 1 : INITTEST;
483 : :
484 : : /* Keep both directly written operands in string mode from creation */
485 : 1 : m_create(char,first,MEMORY_STRING);
486 : 1 : m_create(char,second,MEMORY_STRING);
487 : :
488 : : /* Reserve writable capacity for the two strings and their terminators */
489 : 1 : ASSERT(SUCCESS == m_resize(first,16));
490 : 1 : ASSERT(SUCCESS == m_resize(second,16));
491 : :
492 : : /* Obtain raw views for the intentionally direct writes shown in the README */
493 : 1 : char *first_buf = m_raw_data(first);
494 : 1 : char *second_buf = m_raw_data(second);
495 : 1 : ASSERT(first_buf != NULL);
496 : 1 : ASSERT(second_buf != NULL);
497 : :
498 : 1 : int first_written = -1;
499 : 1 : int second_written = -1;
500 : :
501 : 1 : IF(first_buf != NULL && second_buf != NULL)
502 : : {
503 : 1 : first_written = snprintf(first_buf,first->length,"Hello");
504 : 1 : second_written = snprintf(second_buf,second->length," world!");
505 : : }
506 : :
507 : 1 : ASSERT(first_written >= 0);
508 : 1 : ASSERT(second_written >= 0);
509 : 1 : ASSERT((size_t)first_written < first->length);
510 : 1 : ASSERT((size_t)second_written < second->length);
511 : :
512 : : /* Publish the visible lengths before concatenation consumes their cached values */
513 : 1 : ASSERT(SUCCESS == m_finalize_string(first,(size_t)first_written));
514 : 1 : ASSERT(SUCCESS == m_finalize_string(second,(size_t)second_written));
515 : 1 : ASSERT(SUCCESS == m_concat_strings(first,second));
516 : :
517 : : /* Validate the concatenated descriptor before printing the documented result */
518 : 1 : ASSERT(strcmp(m_text(first),"Hello world!") == 0);
519 : :
520 : : /* The enclosing test matches this printed result against the README promise */
521 : 1 : printf("%s\n",m_text(first));
522 : :
523 : 1 : call(m_del(first));
524 : 1 : call(m_del(second));
525 : :
526 : 1 : deliver(status);
527 : : }
528 : :
529 : : /**
530 : : * @brief Check README example 14 stdout and descriptor behavior
531 : : *
532 : : * Captures the documented construction scenario output and verifies that the
533 : : * finalized descriptors produce exactly the concatenated string promised by
534 : : * the example
535 : : *
536 : : * @return Return describing success or failure
537 : : */
538 : 1 : static Return test_libmem_0000_14(void)
539 : : {
540 : 1 : INITTEST;
541 : :
542 : : static const char expected_stdout_pattern_libmem_0000_14[] =
543 : : "\\A"
544 : : "Hello world!\n"
545 : : "\\Z";
546 : :
547 : 1 : ASSERT(SUCCESS == match_function_output(expected_stdout_pattern_libmem_0000_14,NULL,test_libmem_0000_14_body));
548 : :
549 : 1 : RETURN_STATUS;
550 : : }
551 : :
552 : : /**
553 : : * @brief Run README example 15 that prints a safe string view and empty length
554 : : *
555 : : * Builds one finalized byte string for regular read-only access and keeps a
556 : : * second string descriptor empty. The soft m_text(...) accessor must expose
557 : : * both the completed text and a readable empty fallback view, while
558 : : * m_string_length(...) reports zero visible elements for the empty descriptor
559 : : *
560 : : * @return Return describing success or failure
561 : : */
562 : 1 : static Return test_libmem_0000_15_body(void)
563 : : {
564 : 1 : INITTEST;
565 : :
566 : 1 : m_create(char,buffer,MEMORY_STRING);
567 : 1 : m_create(char,scratch,MEMORY_STRING);
568 : 1 : size_t scratch_length = 0;
569 : :
570 : : /* Reserve writable capacity before obtaining the pointer used for direct construction */
571 : 1 : ASSERT(SUCCESS == m_resize(buffer,32,ZERO_NEW_MEMORY));
572 : :
573 : 1 : char *writable = m_data(char,buffer);
574 : 1 : ASSERT(writable != NULL);
575 : :
576 : 1 : int written = -1;
577 : :
578 : 1 : IF(writable != NULL)
579 : : {
580 : 1 : written = snprintf(writable,buffer->length,"Hello world!");
581 : : }
582 : :
583 : 1 : ASSERT(written >= 0);
584 : 1 : ASSERT((size_t)written < buffer->length);
585 : 1 : ASSERT(SUCCESS == m_finalize_string(buffer,(size_t)written));
586 : :
587 : : /* Expose the finalized byte string through the soft read-only accessor */
588 : 1 : const char *view = m_text(buffer);
589 : 1 : ASSERT(view != NULL);
590 : 1 : ASSERT(strcmp(view,"Hello world!") == 0);
591 : :
592 : 1 : printf("%s\n",view);
593 : :
594 : : /* An empty initialized string still exposes an empty read-only string and zero length */
595 : 1 : const char *scratch_view = m_text(scratch);
596 : 1 : ASSERT(scratch_view != NULL);
597 : 1 : ASSERT(strcmp(scratch_view,"") == 0);
598 : 1 : printf("scratch text: \"%s\"\n",scratch_view);
599 : :
600 : 1 : ASSERT(SUCCESS == m_string_length(scratch,&scratch_length));
601 : 1 : ASSERT(scratch_length == 0U);
602 : :
603 : 1 : printf("scratch length: %zu\n",scratch_length);
604 : :
605 : 1 : call(m_del(buffer));
606 : 1 : call(m_del(scratch));
607 : :
608 : 1 : deliver(status);
609 : : }
610 : :
611 : : /**
612 : : * @brief Check README example 15 stdout with empty text and length
613 : : *
614 : : * Captures the printed normal view, empty fallback view, and empty-string
615 : : * length after its in-function assertions validate both m_text(...) results
616 : : *
617 : : * @return Return describing success or failure
618 : : */
619 : 1 : static Return test_libmem_0000_15(void)
620 : : {
621 : 1 : INITTEST;
622 : :
623 : : static const char expected_stdout_pattern_libmem_0000_15[] =
624 : : "\\A"
625 : : "Hello world!\n"
626 : : "scratch text: \"\"\n"
627 : : "scratch length: 0\n"
628 : : "\\Z";
629 : :
630 : 1 : ASSERT(SUCCESS == match_function_output(expected_stdout_pattern_libmem_0000_15,NULL,test_libmem_0000_15_body));
631 : :
632 : 1 : RETURN_STATUS;
633 : : }
634 : :
635 : : /**
636 : : * @brief Check README example 16 for formatted message construction
637 : : *
638 : : * Builds a human-readable file-size message from string and numeric format
639 : : * arguments. The formatted helper must keep the destination in string mode,
640 : : * cache the visible rendered length, and expose the complete resulting text
641 : : *
642 : : * @return Return describing success or failure
643 : : */
644 : 1 : static Return test_libmem_0000_16(void)
645 : : {
646 : 1 : INITTEST;
647 : :
648 : 1 : m_create(char,message,MEMORY_STRING);
649 : :
650 : 1 : const char *name = "archive.tar";
651 : 1 : const size_t size = 4096;
652 : :
653 : 1 : ASSERT(SUCCESS == m_formatted_string(message,"File %s: %zu bytes",name,size));
654 : 1 : ASSERT(message->is_string == true);
655 : 1 : ASSERT(message->string_length == strlen("File archive.tar: 4096 bytes"));
656 : 1 : ASSERT(strcmp(m_text(message),"File archive.tar: 4096 bytes") == 0);
657 : :
658 : 1 : call(m_del(message));
659 : :
660 : 1 : RETURN_STATUS;
661 : : }
662 : :
663 : : /**
664 : : * @brief Check README example 17 for bounded copy and bounded append
665 : : *
666 : : * Copies a database path and appends a suffix from two source buffers whose
667 : : * readable ranges contain bytes after their first zero terminators. The
668 : : * bounded string helpers must keep only each visible prefix and leave the
669 : : * destination as one coherent terminated string
670 : : *
671 : : * @return Return describing success or failure
672 : : */
673 : 1 : static Return test_libmem_0000_17(void)
674 : : {
675 : 1 : INITTEST;
676 : :
677 : 1 : m_create(char,db_path,MEMORY_STRING);
678 : :
679 : 1 : const char in_memory_db_path[] = {':','m','e','m','o','r','y',':','\0','x','x'};
680 : 1 : const char limited_suffix[] = {'-','n','e','w','\0','x','x'};
681 : :
682 : : /* Bounded copy stops at the first terminator and ignores readable tail bytes */
683 : 1 : ASSERT(SUCCESS == m_copy_string(db_path,sizeof(in_memory_db_path),in_memory_db_path));
684 : 1 : ASSERT(db_path->is_string == true);
685 : 1 : ASSERT(db_path->string_length == strlen(":memory:"));
686 : 1 : ASSERT(db_path->length == strlen(":memory:") + 1U);
687 : 1 : ASSERT(strcmp(m_text(db_path),":memory:") == 0);
688 : :
689 : : /* Bounded append applies the same visible-prefix rule to a suffix buffer */
690 : 1 : ASSERT(SUCCESS == m_concat_string(db_path,sizeof(limited_suffix),limited_suffix));
691 : 1 : ASSERT(db_path->is_string == true);
692 : 1 : ASSERT(db_path->string_length == strlen(":memory:-new"));
693 : 1 : ASSERT(db_path->length == strlen(":memory:-new") + 1U);
694 : 1 : ASSERT(strcmp(m_text(db_path),":memory:-new") == 0);
695 : :
696 : 1 : call(m_del(db_path));
697 : :
698 : 1 : RETURN_STATUS;
699 : : }
700 : :
701 : : /**
702 : : * @brief Check README example 18 for direct write followed by finalization
703 : : *
704 : : * Allocates room for a terminated draft string, writes it through the checked
705 : : * writable view, and finalizes the descriptor so its cached visible length
706 : : * agrees with the directly written payload
707 : : *
708 : : * @return Return describing success or failure
709 : : */
710 : 1 : static Return test_libmem_0000_18(void)
711 : : {
712 : 1 : INITTEST;
713 : :
714 : 1 : m_create(char,title,MEMORY_STRING);
715 : :
716 : 1 : const char draft[] = "draft";
717 : :
718 : : /* Prepare string storage and write a complete terminated C string
719 : : through the checked writable view */
720 : 1 : ASSERT(SUCCESS == m_resize(title,sizeof(draft)));
721 : :
722 : 1 : char *title_view = m_data(char,title);
723 : 1 : ASSERT(title_view != NULL);
724 : :
725 : 1 : IF(title_view != NULL)
726 : : {
727 : 1 : memcpy(title_view,draft,sizeof(draft));
728 : : }
729 : :
730 : : /* Synchronize cached string metadata after the direct buffer write */
731 : 1 : ASSERT(SUCCESS == m_finalize_string(title,sizeof(draft) - 1U));
732 : 1 : ASSERT(title->is_string == true);
733 : 1 : ASSERT(title->string_length == strlen("draft"));
734 : 1 : ASSERT(title->length == sizeof(draft));
735 : 1 : ASSERT(strcmp(m_text(title),"draft") == 0);
736 : :
737 : 1 : call(m_del(title));
738 : :
739 : 1 : RETURN_STATUS;
740 : : }
741 : :
742 : : /**
743 : : * @brief Check README example 19 for unbounded and bounded string array append
744 : : *
745 : : * Builds a root data descriptor that owns three inline string descriptors.
746 : : * It covers both zero-terminated and bounded sources, confirms that each
747 : : * child remains a char string descriptor, and deletes all owned storage
748 : : * through the array cleanup helper
749 : : *
750 : : * @return Return describing success or failure
751 : : */
752 : 1 : static Return test_libmem_0000_19(void)
753 : : {
754 : 1 : INITTEST;
755 : :
756 : 1 : m_create(memory,names);
757 : :
758 : 1 : const char source[] = {'z','e','t','a','\0','x'};
759 : :
760 : : /* Append ordinary zero-terminated strings as owned child descriptors */
761 : 1 : ASSERT(SUCCESS == m_string_array_append(names,char,"delta"));
762 : 1 : ASSERT(SUCCESS == m_string_array_append(names,char,"epsilon"));
763 : :
764 : : /* Append only the visible prefix of a bounded source with tail bytes */
765 : 1 : ASSERT(SUCCESS == m_string_array_append(names,char,sizeof(source),source));
766 : 1 : ASSERT(names->is_string == false);
767 : 1 : ASSERT(names->single_element_size == sizeof(memory));
768 : 1 : ASSERT(names->length == 3U);
769 : :
770 : 1 : const memory *items = m_data_ro(memory,names);
771 : 1 : ASSERT(items != NULL);
772 : :
773 : 1 : IF(items != NULL)
774 : : {
775 : : /* Each array element is an owned char string descriptor */
776 : 1 : ASSERT(items[0].is_string == true);
777 : 1 : ASSERT(items[0].single_element_size == sizeof(char));
778 : 1 : ASSERT(strcmp(m_text(&items[0]),"delta") == 0);
779 : 1 : ASSERT(items[1].is_string == true);
780 : 1 : ASSERT(items[1].single_element_size == sizeof(char));
781 : 1 : ASSERT(strcmp(m_text(&items[1]),"epsilon") == 0);
782 : 1 : ASSERT(items[2].is_string == true);
783 : 1 : ASSERT(items[2].single_element_size == sizeof(char));
784 : 1 : ASSERT(strcmp(m_text(&items[2]),"zeta") == 0);
785 : : }
786 : :
787 : : /* Delete the child strings and their root descriptor in one operation */
788 : 1 : call(m_array_del(names));
789 : 1 : ASSERT(names->data == NULL);
790 : 1 : ASSERT(names->length == 0U);
791 : 1 : ASSERT(names->string_length == 0U);
792 : :
793 : 1 : RETURN_STATUS;
794 : : }
795 : :
796 : : /**
797 : : * @brief Run README example 20 that iterates over a string descriptor array
798 : : *
799 : : * Appends three child strings, visits them through m_string_array_foreach(...),
800 : : * prints them in insertion order, and counts the visited descriptors
801 : : *
802 : : * @return Return describing success or failure
803 : : */
804 : 1 : static Return test_libmem_0000_20_body(void)
805 : : {
806 : 1 : INITTEST;
807 : :
808 : 1 : m_create(memory,names);
809 : :
810 : 1 : ASSERT(SUCCESS == m_string_array_append(names,char,"alpha"));
811 : 1 : ASSERT(SUCCESS == m_string_array_append(names,char,"beta"));
812 : 1 : ASSERT(SUCCESS == m_string_array_append(names,char,"gamma"));
813 : :
814 : 1 : size_t item_count = 0;
815 : :
816 [ + + + + ]: 5 : m_string_array_foreach(names,item)
817 : : {
818 : 3 : printf("%s\n",m_text(item));
819 : 3 : ++item_count;
820 : : }
821 : :
822 : 1 : ASSERT(item_count == 3U);
823 : :
824 : 1 : call(m_array_del(names));
825 : :
826 : 1 : deliver(status);
827 : : }
828 : :
829 : : /**
830 : : * @brief Check README example 20 stdout and string array traversal
831 : : *
832 : : * Captures the README example output and requires all appended strings to be
833 : : * printed once in insertion order
834 : : *
835 : : * @return Return describing success or failure
836 : : */
837 : 1 : static Return test_libmem_0000_20(void)
838 : : {
839 : 1 : INITTEST;
840 : :
841 : : static const char expected_stdout_pattern_libmem_0000_20[] =
842 : : "\\A"
843 : : "alpha\n"
844 : : "beta\n"
845 : : "gamma\n"
846 : : "\\Z";
847 : :
848 : 1 : ASSERT(SUCCESS == match_function_output(expected_stdout_pattern_libmem_0000_20,NULL,test_libmem_0000_20_body));
849 : :
850 : 1 : RETURN_STATUS;
851 : : }
852 : :
853 : : /**
854 : : * @brief Check README example 21 for resize flags
855 : : *
856 : : * Grows a typed point descriptor past one allocation block while requiring
857 : : * the newly exposed payload to be zeroed, then shrinks it to a small prefix
858 : : * with RELEASE_UNUSED and verifies that the excess reserve is returned
859 : : *
860 : : * @return Return describing success or failure
861 : : */
862 : 1 : static Return test_libmem_0000_21(void)
863 : : {
864 : 1 : INITTEST;
865 : :
866 : 1 : m_create(point,points);
867 : :
868 : 1 : const size_t points_per_block = MEMORY_BLOCK_BYTES / sizeof(point);
869 : 1 : const size_t large_length = points_per_block + 1U;
870 : 1 : const size_t surviving_length = 4U;
871 : :
872 : 1 : ASSERT(points_per_block > 0U);
873 : 1 : ASSERT(large_length > surviving_length);
874 : :
875 : : /* Grow across a reserve-block boundary and zero all newly exposed points */
876 : 1 : ASSERT((CRITICAL & m_resize(points,large_length,ZERO_NEW_MEMORY)) == 0);
877 : 1 : ASSERT(points->length == large_length);
878 : 1 : const size_t allocated_before_shrink = points->actually_allocated_bytes;
879 : 1 : ASSERT(allocated_before_shrink > MEMORY_BLOCK_BYTES);
880 : :
881 : 1 : point *items = m_data(point,points);
882 : 1 : ASSERT(items != NULL);
883 : :
884 : 1 : IF(items != NULL)
885 : : {
886 [ + + ]: 514 : for(size_t i = 0; i < points->length; ++i)
887 : : {
888 : 513 : ASSERT(items[i].x == 0);
889 : 513 : ASSERT(items[i].y == 0);
890 : : }
891 : : }
892 : :
893 : : /* Shrink to a small prefix and require the extra reserve block to be returned */
894 : 1 : ASSERT((CRITICAL & m_resize(points,surviving_length,RELEASE_UNUSED)) == 0);
895 : 1 : ASSERT(points->length == surviving_length);
896 : 1 : ASSERT(points->data != NULL);
897 : 1 : ASSERT(points->actually_allocated_bytes == MEMORY_BLOCK_BYTES);
898 : 1 : ASSERT(points->actually_allocated_bytes < allocated_before_shrink);
899 : :
900 : 1 : call(m_del(points));
901 : :
902 : 1 : RETURN_STATUS;
903 : : }
904 : :
905 : : /**
906 : : * @brief Verify all numbered C examples maintained in the libmem README
907 : : *
908 : : * This suite is a living index of README examples. Each nested test is
909 : : * numbered so the README can link to the matching regression case directly.
910 : : * The checks intentionally validate both the visible result and the descriptor
911 : : * metadata that makes the example safe: mode, logical length, cached string
912 : : * length, typed access, captured output, and cleanup behavior
913 : : *
914 : : * @return Return describing success or failure
915 : : */
916 : 1 : Return test_libmem_0000(void)
917 : : {
918 : 1 : INITTEST;
919 : :
920 : 1 : TEST(test_libmem_0000_01,"README example 01: data descriptor rejects string copy");
921 : 1 : TEST(test_libmem_0000_02,"README example 02: MEMORY_STRING descriptor accepts string copy");
922 : 1 : TEST(test_libmem_0000_03,"README example 03: m_to_string enables later string copy");
923 : 1 : TEST(test_libmem_0000_04,"README example 04: typed point self-append survives destination storage growth");
924 : 1 : TEST(test_libmem_0000_05,"README example 05: data-to-string conversion reuses spare reserved storage");
925 : 1 : TEST(test_libmem_0000_06,"README example 06: descriptor can be reused after m_del");
926 : 1 : TEST(test_libmem_0000_07,"README example 07: copied raw bytes reuse their existing terminator as a string");
927 : 1 : TEST(test_libmem_0000_08,"README example 08: literal copy derives the same fixed-string size");
928 : 1 : TEST(test_libmem_0000_09,"README example 09: formatted string renders file name");
929 : 1 : TEST(test_libmem_0000_10,"README example 10: direct rewrite is finalized as a shorter string");
930 : 1 : TEST(test_libmem_0000_11,"README example 11: fixed-size string copy keeps one terminator");
931 : 1 : TEST(test_libmem_0000_12,"README example 12: fixed-size string append extends text");
932 : 1 : TEST(test_libmem_0000_13,"README example 13: literal append matches fixed-string append");
933 : 1 : TEST(test_libmem_0000_14,"README example 14: raw direct writes are finalized before concat");
934 : 1 : TEST(test_libmem_0000_15,"README example 15: m_text exposes text and empty views with zero length");
935 : 1 : TEST(test_libmem_0000_16,"README example 16: formatted message is stored in a descriptor");
936 : 1 : TEST(test_libmem_0000_17,"README example 17: bounded string copy and append ignore tail bytes");
937 : 1 : TEST(test_libmem_0000_18,"README example 18: direct buffer write becomes a valid string");
938 : 1 : TEST(test_libmem_0000_19,"README example 19: string array append owns nested descriptors");
939 : 1 : TEST(test_libmem_0000_20,"README example 20: string array foreach visits every item");
940 : 1 : TEST(test_libmem_0000_21,"README example 21: m_resize flags zero and release storage");
941 : :
942 : 1 : RETURN_STATUS;
943 : : }
|