pthreadpool: replace assert_return_code(ret, 0); with assert_int_equal(ret, 0);
[samba.git] / lib / pthreadpool / tests_cmocka.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * cmocka tests for thread pool implementation
4  * Copyright (C) Christof Schmitt 2017
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <errno.h>
21 #include <pthread.h>
22 #include <setjmp.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <limits.h>
26
27 #include <talloc.h>
28 #include <tevent.h>
29 #include <pthreadpool_tevent.h>
30
31 #include <cmocka.h>
32 #include <poll.h>
33
34 struct pthreadpool_tevent_test {
35         struct tevent_context *ev;
36         struct pthreadpool_tevent *upool;
37         struct pthreadpool_tevent *spool;
38         struct pthreadpool_tevent *opool;
39 };
40
41 static int setup_pthreadpool_tevent(void **state)
42 {
43         struct pthreadpool_tevent_test *t;
44         int ret;
45         size_t max_threads;
46
47         t = talloc_zero(NULL, struct pthreadpool_tevent_test);
48         assert_non_null(t);
49
50         t->ev = tevent_context_init(t);
51         assert_non_null(t->ev);
52
53         ret = pthreadpool_tevent_init(t->ev, UINT_MAX, &t->upool);
54         assert_int_equal(ret, 0);
55
56         max_threads = pthreadpool_tevent_max_threads(t->upool);
57         assert_int_equal(max_threads, UINT_MAX);
58
59         ret = pthreadpool_tevent_init(t->ev, 1, &t->opool);
60         assert_int_equal(ret, 0);
61
62         max_threads = pthreadpool_tevent_max_threads(t->opool);
63         assert_int_equal(max_threads, 1);
64
65         ret = pthreadpool_tevent_init(t->ev, 0, &t->spool);
66         assert_int_equal(ret, 0);
67
68         max_threads = pthreadpool_tevent_max_threads(t->spool);
69         assert_int_equal(max_threads, 0);
70
71         *state = t;
72
73         return 0;
74 }
75
76 static int teardown_pthreadpool_tevent(void **state)
77 {
78         struct pthreadpool_tevent_test *t = *state;
79
80         TALLOC_FREE(t);
81
82         return 0;
83 }
84
85 int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
86                           void *(*start_routine) (void *), void *arg);
87 int __real_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
88                           void *(*start_routine) (void *),  void *arg);
89
90 int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
91                           void *(*start_routine) (void *), void *arg)
92 {
93         int error;
94
95         error = mock_type(int);
96         if (error != 0) {
97                 return error;
98         }
99
100         return __real_pthread_create(thread, attr, start_routine, arg);
101 }
102
103 static void test_job_threadid(void *ptr)
104 {
105         pthread_t *threadid = ptr;
106
107         *threadid = pthread_self();
108 }
109
110 static int test_create_do(struct tevent_context *ev,
111                           struct pthreadpool_tevent *pool,
112                           bool *executed,
113                           bool *in_main_thread)
114 {
115         struct tevent_req *req;
116         pthread_t zero_thread;
117         pthread_t main_thread;
118         pthread_t worker_thread;
119         bool ok;
120         int ret;
121
122         *executed = false;
123         *in_main_thread = false;
124
125         memset(&zero_thread, 0, sizeof(zero_thread));
126         main_thread = pthread_self();
127         worker_thread = zero_thread;
128
129         req = pthreadpool_tevent_job_send(
130                 ev, ev, pool, test_job_threadid, &worker_thread);
131         if (req == NULL) {
132                 fprintf(stderr, "pthreadpool_tevent_job_send failed\n");
133                 return ENOMEM;
134         }
135
136         ok = tevent_req_poll(req, ev);
137         if (!ok) {
138                 ret = errno;
139                 fprintf(stderr, "tevent_req_poll failed: %s\n",
140                         strerror(ret));
141                 *executed = !pthread_equal(worker_thread, zero_thread);
142                 *in_main_thread = pthread_equal(worker_thread, main_thread);
143                 return ret;
144         }
145
146
147         ret = pthreadpool_tevent_job_recv(req);
148         TALLOC_FREE(req);
149         *executed = !pthread_equal(worker_thread, zero_thread);
150         *in_main_thread = pthread_equal(worker_thread, main_thread);
151         if (ret != 0) {
152                 fprintf(stderr, "tevent_req_recv failed: %s\n",
153                         strerror(ret));
154                 return ret;
155         }
156
157         return 0;
158 }
159
160 static void test_create(void **state)
161 {
162         struct pthreadpool_tevent_test *t = *state;
163         bool executed;
164         bool in_main_thread;
165         int ret;
166
167         /*
168          * When pthreadpool cannot create the first worker thread,
169          * this job will run in the sync fallback in the main thread.
170          */
171         will_return(__wrap_pthread_create, EAGAIN);
172         ret = test_create_do(t->ev, t->upool, &executed, &in_main_thread);
173         assert_int_equal(ret, EAGAIN);
174         assert_false(executed);
175         assert_false(in_main_thread);
176
177         /*
178          * The sync pool won't trigger pthread_create()
179          * It will be triggered by the one pool.
180          */
181         will_return(__wrap_pthread_create, EAGAIN);
182
183         ret = test_create_do(t->ev, t->spool, &executed, &in_main_thread);
184         assert_int_equal(ret, 0);
185         assert_true(executed);
186         assert_true(in_main_thread);
187
188         ret = test_create_do(t->ev, t->opool, &executed, &in_main_thread);
189         assert_int_equal(ret, EAGAIN);
190         assert_false(executed);
191         assert_false(in_main_thread);
192
193         /*
194          * When a thread can be created, the job will run in the worker thread.
195          */
196         will_return(__wrap_pthread_create, 0);
197         ret = test_create_do(t->ev, t->upool, &executed, &in_main_thread);
198         assert_int_equal(ret, 0);
199         assert_true(executed);
200         assert_false(in_main_thread);
201
202         poll(NULL, 0, 10);
203
204         /*
205          * Workerthread will still be active for a second; immediately
206          * running another job will also use the worker thread, even
207          * if a new thread cannot be created.
208          */
209         ret = test_create_do(t->ev, t->upool, &executed, &in_main_thread);
210         assert_int_equal(ret, 0);
211         assert_true(executed);
212         assert_false(in_main_thread);
213
214         /*
215          * When a thread can be created, the job will run in the worker thread.
216          */
217         will_return(__wrap_pthread_create, 0);
218         ret = test_create_do(t->ev, t->opool, &executed, &in_main_thread);
219         assert_int_equal(ret, 0);
220         assert_true(executed);
221         assert_false(in_main_thread);
222
223         poll(NULL, 0, 10);
224
225         /*
226          * Workerthread will still be active for a second; immediately
227          * running another job will also use the worker thread, even
228          * if a new thread cannot be created.
229          */
230         ret = test_create_do(t->ev, t->opool, &executed, &in_main_thread);
231         assert_int_equal(ret, 0);
232         assert_true(executed);
233         assert_false(in_main_thread);
234 }
235
236 int main(int argc, char **argv)
237 {
238         const struct CMUnitTest tests[] = {
239                 cmocka_unit_test_setup_teardown(test_create,
240                                                 setup_pthreadpool_tevent,
241                                                 teardown_pthreadpool_tevent),
242         };
243
244         cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
245
246         return cmocka_run_group_tests(tests, NULL, NULL);
247 }