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