Branch data Line data Source code
1 : : #include "test_libmem_all.h"
2 : :
3 : : /**
4 : : * @brief Verify data-to-string conversion for a manually filled char descriptor
5 : : *
6 : : * Starts with a data-mode char descriptor, writes `xyz` through the typed
7 : : * writable data view, and then converts the descriptor to string mode.
8 : : * The checks prove that conversion appends the missing terminator,
9 : : * caches the visible string length, and keeps the resulting text
10 : : * readable through the soft string view
11 : : *
12 : : * @return Return describing success or failure
13 : : */
14 : 1 : static Return test_libmem_0015_1(void)
15 : : {
16 : 1 : INITTEST;
17 : :
18 : 1 : m_create(char,buffer);
19 : :
20 : : /* Allocate exactly three data bytes, without room for a terminator yet */
21 : 1 : ASSERT(SUCCESS == m_resize(buffer,3));
22 : :
23 : : /* Write a non-terminated payload through the typed writable data view */
24 : 1 : char *bytes = m_data(char,buffer);
25 : 1 : ASSERT(bytes != NULL);
26 : :
27 : 1 : IF(bytes != NULL)
28 : : {
29 : 1 : bytes[0] = 'x';
30 : 1 : bytes[1] = 'y';
31 : 1 : bytes[2] = 'z';
32 : : }
33 : :
34 : : /* Conversion from data mode must measure the payload and add one terminator slot */
35 : 1 : ASSERT(buffer->string_length == 0);
36 : 1 : ASSERT(buffer->is_string == false);
37 : 1 : ASSERT(SUCCESS == m_to_string(buffer));
38 : 1 : ASSERT(buffer->string_length == 3);
39 : 1 : ASSERT(buffer->is_string == true);
40 : 1 : ASSERT(buffer->length == 4);
41 : 1 : ASSERT(0 == strcmp(m_text(buffer),"xyz"));
42 : :
43 : 1 : call(m_del(buffer));
44 : :
45 : 1 : RETURN_STATUS;
46 : : }
47 : :
48 : : /**
49 : : * @brief Verify string resize shrink and zero-length resize with retained reserve
50 : : *
51 : : * Builds a string descriptor containing `xyz`, shrinks it to three
52 : : * logical slots, and verifies that the visible text becomes `xy`.
53 : : * Then the descriptor is resized to zero without RELEASE_UNUSED; the
54 : : * already allocated backing block must stay attached, while string
55 : : * length and logical length are cleared
56 : : *
57 : : * @return Return describing success or failure
58 : : */
59 : 1 : static Return test_libmem_0015_2(void)
60 : : {
61 : 1 : INITTEST;
62 : :
63 : 1 : m_create(char,buffer,MEMORY_STRING);
64 : :
65 : : /* Seed the string descriptor with three visible bytes plus a terminator */
66 : 1 : ASSERT(SUCCESS == m_concat_literal(buffer,"xyz"));
67 : 1 : ASSERT(buffer->string_length == 3);
68 : 1 : ASSERT(buffer->is_string == true);
69 : 1 : ASSERT(buffer->length == 4);
70 : 1 : ASSERT(0 == strcmp(m_text(buffer),"xyz"));
71 : :
72 : : /* Shrinking to three total slots leaves room for only two visible bytes plus the terminator */
73 : 1 : ASSERT(SUCCESS == m_resize(buffer,3));
74 : 1 : ASSERT(buffer->string_length == 2);
75 : 1 : ASSERT(buffer->is_string == true);
76 : 1 : ASSERT(buffer->length == 3);
77 : 1 : ASSERT(0 == strcmp(m_text(buffer),"xy"));
78 : :
79 : : /* Remember the attached allocation; zero resize without RELEASE_UNUSED must keep it reserved */
80 : 1 : void *const retained_string_data = buffer->data;
81 : 1 : const size_t retained_string_bytes = buffer->actually_allocated_bytes;
82 : :
83 : 1 : ASSERT(retained_string_data != NULL);
84 : 1 : ASSERT(retained_string_bytes > 0);
85 : :
86 : : /* Logical zeroing clears the string while keeping the backing block available for reuse */
87 : 1 : ASSERT(SUCCESS == m_resize(buffer,0));
88 : 1 : ASSERT(buffer->data == retained_string_data);
89 : 1 : ASSERT(buffer->actually_allocated_bytes == retained_string_bytes);
90 : 1 : ASSERT(buffer->length == 0);
91 : 1 : ASSERT(buffer->string_length == 0);
92 : 1 : ASSERT(buffer->is_string == true);
93 : 1 : ASSERT(0 == strcmp(m_text(buffer),""));
94 : :
95 : 1 : call(m_del(buffer));
96 : :
97 : 1 : RETURN_STATUS;
98 : : }
99 : :
100 : : /**
101 : : * @brief Verify refill after zero resize and raw writable mutation of string storage
102 : : *
103 : : * Reuses a string descriptor after `m_resize(buffer,0)` without
104 : : * RELEASE_UNUSED, then writes through m_raw_data. The test proves that
105 : : * refill reuses the retained backing block and that raw writes expose
106 : : * the live string storage without recalculating cached metadata
107 : : *
108 : : * @return Return describing success or failure
109 : : */
110 : 1 : static Return test_libmem_0015_3(void)
111 : : {
112 : 1 : INITTEST;
113 : :
114 : 1 : m_create(char,buffer,MEMORY_STRING);
115 : : static const char refill[] = "hi";
116 : :
117 : : /* Create and then logically clear a string while keeping its allocation */
118 : 1 : ASSERT(SUCCESS == m_concat_literal(buffer,"xy"));
119 : 1 : void *const retained_string_data = buffer->data;
120 : 1 : const size_t retained_string_bytes = buffer->actually_allocated_bytes;
121 : :
122 : 1 : ASSERT(retained_string_data != NULL);
123 : 1 : ASSERT(retained_string_bytes > 0);
124 : 1 : ASSERT(SUCCESS == m_resize(buffer,0));
125 : 1 : ASSERT(buffer->data == retained_string_data);
126 : 1 : ASSERT(buffer->actually_allocated_bytes == retained_string_bytes);
127 : :
128 : : /* Refill through the string API and require the previously retained block to be reused */
129 : 1 : ASSERT(SUCCESS == m_concat_fixed_string(buffer,sizeof(refill),refill));
130 : 1 : ASSERT(buffer->data == retained_string_data);
131 : 1 : ASSERT(buffer->actually_allocated_bytes == retained_string_bytes);
132 : 1 : ASSERT(buffer->string_length == 2);
133 : 1 : ASSERT(buffer->is_string == true);
134 : 1 : ASSERT(buffer->length == 3);
135 : 1 : ASSERT(0 == strcmp(m_text(buffer),"hi"));
136 : :
137 : : /* Raw access exposes the live storage but intentionally leaves cached metadata unchanged */
138 : 1 : char *raw_bytes = (char *)m_raw_data(buffer);
139 : 1 : ASSERT(raw_bytes != NULL);
140 : :
141 : 1 : IF(raw_bytes != NULL)
142 : : {
143 : 1 : raw_bytes[0] = 'b';
144 : 1 : raw_bytes[1] = 'y';
145 : 1 : raw_bytes[2] = '\0';
146 : : }
147 : :
148 : : /* The same string descriptor now reads as "by" while its cached length remains valid */
149 : 1 : ASSERT(buffer->data == retained_string_data);
150 : 1 : ASSERT(buffer->string_length == 2);
151 : 1 : ASSERT(buffer->is_string == true);
152 : 1 : ASSERT(buffer->length == 3);
153 : 1 : ASSERT(0 == strcmp(m_text(buffer),"by"));
154 : :
155 : 1 : call(m_del(buffer));
156 : :
157 : 1 : RETURN_STATUS;
158 : : }
159 : :
160 : : /**
161 : : * @brief Verify m_del clears string descriptor storage while preserving string mode
162 : : *
163 : : * Deletes a non-empty string descriptor and checks the postcondition:
164 : : * allocated storage and logical counters are cleared, while the
165 : : * descriptor's string mode flag remains set for possible later reuse
166 : : *
167 : : * @return Return describing success or failure
168 : : */
169 : 1 : static Return test_libmem_0015_4(void)
170 : : {
171 : 1 : INITTEST;
172 : :
173 : 1 : m_create(char,buffer,MEMORY_STRING);
174 : :
175 : : /* Make deletion meaningful by giving the descriptor live string storage first */
176 : 1 : ASSERT(SUCCESS == m_concat_literal(buffer,"hi"));
177 : 1 : ASSERT(buffer->data != NULL);
178 : 1 : ASSERT(buffer->actually_allocated_bytes > 0);
179 : :
180 : : /* m_del frees storage and lengths, but preserves the descriptor mode */
181 : 1 : call(m_del(buffer));
182 : 1 : ASSERT(buffer->length == 0);
183 : 1 : ASSERT(buffer->string_length == 0);
184 : 1 : ASSERT(buffer->is_string == true);
185 : 1 : ASSERT(buffer->data == NULL);
186 : 1 : ASSERT(buffer->actually_allocated_bytes == 0);
187 : :
188 : 1 : RETURN_STATUS;
189 : : }
190 : :
191 : : /**
192 : : * @brief Verify string conversion, resizing, raw access, and cleanup metadata
193 : : *
194 : : * In plain terms, this suite follows the life of a small char string
195 : : * descriptor through the operations that are easiest to desynchronize:
196 : : * converting raw bytes into a string, shrinking a string while keeping
197 : : * its cached length correct, resizing to zero without releasing the
198 : : * reserved block, refilling the retained block, mutating the live
199 : : * storage through m_raw_data, and finally deleting the descriptor.
200 : : * The nested tests keep those contracts separate so a failure points at
201 : : * the specific state transition that broke cached string metadata
202 : : *
203 : : * @return Return describing success or failure
204 : : */
205 : 1 : Return test_libmem_0015(void)
206 : : {
207 : 1 : INITTEST;
208 : :
209 : 1 : TEST(test_libmem_0015_1,"m_to_string finalizes manually filled char data");
210 : 1 : TEST(test_libmem_0015_2,"String resize shrink and zero resize keep metadata coherent");
211 : 1 : TEST(test_libmem_0015_3,"Refill after zero resize and raw mutation reuse live storage");
212 : 1 : TEST(test_libmem_0015_4,"m_del clears string storage while preserving string mode");
213 : :
214 : 1 : RETURN_STATUS;
215 : : }
|