1 #include <test.h>
2
3 #include <alloc.h>
4 #include <mutex.h>
5 #include <threaded_deque.h>
6
7 /* Memory illustration legend: *
8 * | : memory bounds *
9 * > : left (first non-empty index of data) *
10 * < : right (first empty index after data) *
11 * ^ : left + right (empty) *
12 * v : left + right (at capacity) *
13 * x : used memory *
14 * - : unused memory */
15
test_push_pop(void)16 static void test_push_pop(void)
17 {
18 // Initialised with DEFAULT_CAPACITY = 16
19 ThreadedDeque *deque = ThreadedDequeNew(0, free);
20 // |^---------------|
21 ThreadedDequePushLeft(deque, xstrdup("1"));
22 // |<-------------->|
23 ThreadedDequePushRight(deque, xstrdup("2"));
24 // |x<------------->|
25 ThreadedDequePushLeft(deque, xstrdup("3"));
26 // |x<------------>x|
27
28 char *str1; ThreadedDequePopRight(deque, (void **)&str1, 0);
29 // |<------------->x|
30 char *str2; ThreadedDequePopLeft(deque, (void **)&str2, 0);
31 // |<-------------->|
32 char *str3; ThreadedDequePopRight(deque, (void **)&str3, 0);
33 // |---------------^|
34
35 assert_string_equal(str1, "2");
36 assert_string_equal(str2, "3");
37 assert_string_equal(str3, "1");
38
39 free(str1);
40 free(str2);
41 free(str3);
42
43 ThreadedDequeDestroy(deque);
44 }
45
test_pop_empty_and_push_null(void)46 static void test_pop_empty_and_push_null(void)
47 {
48 ThreadedDeque *deque = ThreadedDequeNew(1, NULL);
49 // |^|
50
51 assert(ThreadedDequeIsEmpty(deque));
52
53 void *i_am_null = NULL;
54 bool ret = ThreadedDequePopLeft(deque, &i_am_null, 0);
55
56 // |^|
57 assert(i_am_null == NULL);
58 assert_false(ret);
59 ThreadedDequePushLeft(deque, i_am_null);
60 // |v|
61 ret = ThreadedDequePopLeft(deque, &i_am_null, 0);
62 assert(i_am_null == NULL);
63 assert_true(ret);
64 // |^|
65
66 ret = ThreadedDequePopRight(deque, &i_am_null, 0);
67
68 // |^|
69 assert(i_am_null == NULL);
70 assert_false(ret);
71 ThreadedDequePushRight(deque, i_am_null);
72 // |v|
73 ret = ThreadedDequePopRight(deque, &i_am_null, 0);
74 assert(i_am_null == NULL);
75 assert_true(ret);
76 // |^|
77
78 ThreadedDequeDestroy(deque);
79 }
80
test_copy(void)81 static void test_copy(void)
82 {
83 ThreadedDeque *deque = ThreadedDequeNew(4, free);
84 // deque: |^---|
85
86 ThreadedDequePushRight(deque, xstrdup("1"));
87 // deque: |><--|
88 ThreadedDequePushRight(deque, xstrdup("2"));
89 // deque: |>x<-|
90 ThreadedDequePushRight(deque, xstrdup("3"));
91 // deque: |>xx<|
92
93 ThreadedDeque *new_deque = ThreadedDequeCopy(deque);
94 // new_deque: |>xx<|
95
96 assert(new_deque != NULL);
97 assert_int_equal(ThreadedDequeCount(deque),
98 ThreadedDequeCount(new_deque));
99 assert_int_equal(ThreadedDequeCapacity(deque),
100 ThreadedDequeCapacity(new_deque));
101
102 char *old_str1; ThreadedDequePopLeft(deque, (void **)&old_str1, 0);
103 // deque: |->x<|
104 char *old_str2; ThreadedDequePopLeft(deque, (void **)&old_str2, 0);
105 // deque: |--><|
106 char *old_str3; ThreadedDequePopLeft(deque, (void **)&old_str3, 0);
107 // deque: |---^|
108
109 char *new_str1; ThreadedDequePopLeft(new_deque, (void **)&new_str1, 0);
110 // new_deque: |->x<|
111 char *new_str2; ThreadedDequePopLeft(new_deque, (void **)&new_str2, 0);
112 // new_deque: |--><|
113 char *new_str3; ThreadedDequePopLeft(new_deque, (void **)&new_str3, 0);
114 // new_deque: |---^|
115
116 // Check if pointers are equal (since this is a shallow copy)
117 assert(old_str1 == new_str1);
118 assert(old_str2 == new_str2);
119 assert(old_str3 == new_str3);
120
121 free(old_str1);
122 free(old_str2);
123 free(old_str3);
124
125 ThreadedDequeSoftDestroy(deque);
126
127 // Tests expanding the copied deque
128 ThreadedDequePushLeft(new_deque, xstrdup("1"));
129 // new_deque: |--><|
130 ThreadedDequePushRight(new_deque, xstrdup("2"));
131 // new_deque: |<->x|
132 ThreadedDequePushLeft(new_deque, xstrdup("3"));
133 // new_deque: |<>xx|
134 ThreadedDequePushRight(new_deque, xstrdup("4"));
135 // new_deque: |xvxx|
136 ThreadedDequePushLeft(new_deque, xstrdup("5"));
137 // Internal array restructured, array moved to end:
138 // new_deque: |>xxxx<--|
139
140 assert_int_equal(ThreadedDequeCount(new_deque), 5);
141 assert_int_equal(ThreadedDequeCapacity(new_deque), 8);
142
143 ThreadedDequePopRight(new_deque, (void **)&new_str1, 0);
144 // new_deque: |>xxx<---|
145 ThreadedDequePopLeft(new_deque, (void **)&new_str2, 0);
146 // new_deque: |->xx<---|
147 ThreadedDequePopRight(new_deque, (void **)&new_str3, 0);
148 // new_deque: |->x<----|
149 char *new_str4; ThreadedDequePopLeft(new_deque, (void **)&new_str4, 0);
150 // new_deque: |--><----|
151 char *new_str5; ThreadedDequePopRight(new_deque, (void **)&new_str5, 0);
152 // new_deque: |--^-----|
153
154 assert_string_equal(new_str1, "4");
155 assert_string_equal(new_str2, "5");
156 assert_string_equal(new_str3, "2");
157 assert_string_equal(new_str4, "3");
158 assert_string_equal(new_str5, "1");
159
160 free(new_str1);
161 free(new_str2);
162 free(new_str3);
163 free(new_str4);
164 free(new_str5);
165
166 ThreadedDequeDestroy(new_deque);
167 }
168
test_push_report_count(void)169 static void test_push_report_count(void)
170 {
171 ThreadedDeque *deque = ThreadedDequeNew(0, free);
172
173 size_t size1 = ThreadedDequePushLeft(deque, xstrdup("1"));
174 size_t size2 = ThreadedDequePushLeft(deque, xstrdup("2"));
175 size_t size3 = ThreadedDequePushLeft(deque, xstrdup("3"));
176 size_t size4 = ThreadedDequePushLeft(deque, xstrdup("4"));
177
178 assert_int_equal(size1, 1);
179 assert_int_equal(size2, 2);
180 assert_int_equal(size3, 3);
181 assert_int_equal(size4, 4);
182
183 ThreadedDequeDestroy(deque);
184 }
185
test_expand(void)186 static void test_expand(void)
187 {
188 ThreadedDeque *deque = ThreadedDequeNew(1, free);
189 // |^|
190
191 ThreadedDequePushRight(deque, xstrdup("spam"));
192 // |v|
193 ThreadedDequePushRight(deque, xstrdup("spam"));
194 // |vx|
195
196 char *tmp; ThreadedDequePopLeft(deque, (void **)&tmp, 0);
197 // |<>|
198 free(tmp);
199
200 ThreadedDequePushLeft(deque, xstrdup("spam"));
201 // |vx|
202 ThreadedDequePushLeft(deque, xstrdup("spam"));
203 // Internal array restructured:
204 // |xx<>|
205 ThreadedDequePushLeft(deque, xstrdup("spam"));
206 // |xxvx|
207 ThreadedDequePushLeft(deque, xstrdup("spam"));
208 // Internal array restructured:
209 // |->xxxx<-|
210 ThreadedDequePushRight(deque, xstrdup("spam"));
211 // |->xxxxx<|
212
213 ThreadedDequePopLeft(deque, (void **)&tmp, 0);
214 // |-->xxxx<|
215 free(tmp);
216 ThreadedDequePopLeft(deque, (void **)&tmp, 0);
217 // |--->xxx<|
218 free(tmp);
219
220 ThreadedDequePushRight(deque, xstrdup("spam"));
221 // |<-->xxxx|
222 ThreadedDequePushLeft(deque, xstrdup("spam"));
223 // |<->xxxxx|
224 ThreadedDequePushRight(deque, xstrdup("spam"));
225 // |x<>xxxxx|
226 ThreadedDequePushLeft(deque, xstrdup("spam"));
227 // |xxvxxxxx|
228 ThreadedDequePushLeft(deque, xstrdup("spam"));
229 // Internal array restructured
230 // |->xxxxxxxx<-----|
231 ThreadedDequePushRight(deque, xstrdup("spam"));
232 // |->xxxxxxxxx<----|
233 ThreadedDequePushLeft(deque, xstrdup("spam"));
234 // |>xxxxxxxxxx<----|
235
236 assert_int_equal(ThreadedDequeCount(deque), 11);
237 assert_int_equal(ThreadedDequeCapacity(deque), 16);
238
239 ThreadedDequeDestroy(deque);
240 }
241
test_popn(void)242 static void test_popn(void)
243 {
244 ThreadedDeque *deque = ThreadedDequeNew(0, free);
245 // Initialised with default size 16
246 // |^---------------|
247
248 char *strs[] = {"spam1", "spam2", "spam3", "spam4", "spam5"};
249
250 for (int i = 0; i < 5; i++)
251 {
252 ThreadedDequePushLeft(deque, xstrdup(strs[i]));
253 }
254 // |<---------->xxxx|
255
256 void **data = NULL;
257 size_t count = ThreadedDequePopRightN(deque, &data, 5, 0);
258 // |-----------^----|
259
260 for (size_t i = 0; i < count; i++)
261 {
262 assert_string_equal(data[i], strs[i]);
263 free(data[i]);
264 }
265
266 free(data);
267 data = NULL;
268
269 for (int i = 0; i < 5; i++)
270 {
271 ThreadedDequePushRight(deque, xstrdup(strs[i]));
272 }
273 // |<---------->xxxx|
274
275 count = ThreadedDequePopLeftN(deque, &data, 5, 0);
276 // |^---------------|
277
278 for (size_t i = 0; i < count; i++)
279 {
280 assert_string_equal(data[i], strs[i]);
281 free(data[i]);
282 }
283
284 free(data);
285 ThreadedDequeDestroy(deque);
286 }
287
288 // Thread tests
289 static ThreadedDeque *thread_deque;
290
thread_pop()291 static void *thread_pop()
292 {
293 char *tmp;
294 ThreadedDequePopLeft(thread_deque, (void **)&tmp, THREAD_BLOCK_INDEFINITELY);
295 assert_string_equal(tmp, "bla");
296 free(tmp);
297
298 return NULL;
299 }
300
thread_push()301 static void *thread_push()
302 {
303 char *str = "bla";
304 ThreadedDequePushLeft(thread_deque, xstrdup(str));
305
306 return NULL;
307 }
308
thread_wait_empty()309 static void *thread_wait_empty()
310 {
311 ThreadedDequeWaitEmpty(thread_deque, THREAD_BLOCK_INDEFINITELY);
312 ThreadedDequePushLeft(thread_deque, xstrdup("a_test"));
313
314 return NULL;
315 }
316
test_threads_wait_pop(void)317 static void test_threads_wait_pop(void)
318 {
319 #define POP_ITERATIONS 100
320 thread_deque = ThreadedDequeNew(0, free);
321
322 pthread_t pops[POP_ITERATIONS] = {0};
323 for (int i = 0; i < POP_ITERATIONS; i++)
324 {
325 int res_create = pthread_create(&(pops[i]), NULL,
326 thread_pop, NULL);
327 assert_int_equal(res_create, 0);
328 }
329
330 pthread_t pushs[POP_ITERATIONS] = {0};
331 for (int i = 0; i < POP_ITERATIONS; i++)
332 {
333 int res_create = pthread_create(&(pushs[i]), NULL,
334 thread_push, NULL);
335 assert_int_equal(res_create, 0);
336 }
337
338 void *retval = NULL;
339 int res;
340
341 for (int i = 0; i < POP_ITERATIONS; i++)
342 {
343 res = pthread_join(pops[i], retval);
344 assert_int_equal(res, 0);
345 assert(retval == NULL);
346
347 res = pthread_join(pushs[i], retval);
348 assert_int_equal(res, 0);
349 assert(retval == NULL);
350 }
351
352 ThreadedDequeDestroy(thread_deque);
353 }
354
test_threads_wait_empty(void)355 static void test_threads_wait_empty(void)
356 {
357 #define WAIT_ITERATIONS 100
358 thread_deque = ThreadedDequeNew(0, free);
359
360 pthread_t pushs[WAIT_ITERATIONS] = {0};
361 for (int i = 0; i < WAIT_ITERATIONS; i++)
362 {
363 int res_create = pthread_create(&(pushs[i]), NULL,
364 thread_push, NULL);
365 assert_int_equal(res_create, 0);
366 }
367
368 sleep(1);
369 pthread_t wait_thread = 0;
370 int res_create = pthread_create(&wait_thread, NULL,
371 thread_wait_empty, NULL);
372 assert_int_equal(res_create, 0);
373
374 do {
375 sleep(1);
376 } while (ThreadedDequeCount(thread_deque) != WAIT_ITERATIONS);
377
378 char **data_array = NULL;
379 size_t arr_size = ThreadedDequePopLeftN(thread_deque,
380 (void ***)&data_array,
381 WAIT_ITERATIONS, 0);
382
383 for (size_t i = 0; i < arr_size; i++)
384 {
385 free(data_array[i]);
386 }
387
388 free(data_array);
389
390 char *waited_str; ThreadedDequePopLeft(thread_deque, (void **)&waited_str, 1);
391 assert_string_equal(waited_str, "a_test");
392 free(waited_str);
393
394 void *retval = NULL;
395 int res;
396
397 for (int i = 0; i < WAIT_ITERATIONS; i++)
398 {
399 res = pthread_join(pushs[i], retval);
400 assert_int_equal(res, 0);
401 assert(retval == NULL);
402 }
403
404 res = pthread_join(wait_thread, retval);
405 assert_int_equal(res, 0);
406 assert(retval == NULL);
407
408 ThreadedDequeDestroy(thread_deque);
409 }
410
main()411 int main()
412 {
413 PRINT_TEST_BANNER();
414 const UnitTest tests[] =
415 {
416 unit_test(test_push_pop),
417 unit_test(test_pop_empty_and_push_null),
418 unit_test(test_copy),
419 unit_test(test_push_report_count),
420 unit_test(test_expand),
421 unit_test(test_popn),
422 unit_test(test_threads_wait_pop),
423 unit_test(test_threads_wait_empty),
424 };
425 return run_tests(tests);
426 }
427