ctdb:tests: Add missing va_end() in ctdb_set_error()
[samba.git] / ctdb / tests / src / ctdb_io_test.c
1 /*
2    ctdb_io tests
3
4    Copyright (C) Christof Schmitt 2019
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 "replace.h"
21 #include "system/filesys.h"
22
23 #include <assert.h>
24
25 #include "common/ctdb_io.c"
26
27 void ctdb_set_error(struct ctdb_context *ctdb, const char *fmt, ...)
28 {
29         va_list ap;
30         va_start(ap, fmt);
31         vprintf(fmt, ap);
32         va_end(ap);
33         assert(false);
34 }
35
36 static void test_setup(ctdb_queue_cb_fn_t cb,
37                        int *pfd,
38                        struct ctdb_context **pctdb,
39                        struct ctdb_queue **pqueue)
40 {
41         int pipefd[2], ret;
42         struct ctdb_context *ctdb;
43         struct ctdb_queue *queue;
44
45         ret = pipe(pipefd);
46         assert(ret == 0);
47
48         ctdb = talloc_zero(NULL, struct ctdb_context);
49         assert(ctdb != NULL);
50
51         ctdb->ev = tevent_context_init(NULL);
52
53         queue = ctdb_queue_setup(ctdb, ctdb, pipefd[0], 0, cb,
54                                  NULL, "test queue");
55         assert(queue != NULL);
56
57         *pctdb = ctdb;
58         *pfd = pipefd[1];
59         if (pqueue != NULL) {
60                 *pqueue = queue;
61         }
62 }
63
64 static const size_t test1_req_len = 8;
65 static const char *test1_req = "abcdefgh";
66
67 static void test1_callback(uint8_t *data, size_t length, void *private_data)
68 {
69         uint32_t len;
70
71         len = *(uint32_t *)data;
72         assert(len == sizeof(uint32_t) + test1_req_len);
73
74         assert(length == sizeof(uint32_t) + test1_req_len);
75         assert(memcmp(data  + sizeof(len), test1_req, test1_req_len) == 0);
76 }
77
78 static void test1(void)
79 {
80         struct ctdb_context *ctdb;
81         int fd, ret;
82         uint32_t pkt_size;
83
84         test_setup(test1_callback, &fd, &ctdb, NULL);
85
86         pkt_size = sizeof(uint32_t) + test1_req_len;
87         ret = write(fd, &pkt_size, sizeof(pkt_size));
88         assert(ret == sizeof(pkt_size));
89
90         ret = write(fd, test1_req, test1_req_len);
91         assert(ret == test1_req_len);
92
93         tevent_loop_once(ctdb->ev);
94
95         TALLOC_FREE(ctdb);
96 }
97
98 static const size_t test2_req_len[] = { 900, 24, 600 };
99
100 static int test2_cb_num = 0;
101
102 static void test2_callback(uint8_t *data, size_t length, void *private_data)
103 {
104         uint32_t len;
105
106         len = *(uint32_t *)data;
107         assert(len == sizeof(uint32_t) + test2_req_len[test2_cb_num]);
108         assert(length == sizeof(uint32_t) + test2_req_len[test2_cb_num]);
109
110         test2_cb_num++;
111 }
112
113 static void test2(void)
114 {
115         struct ctdb_context *ctdb;
116         int fd, ret, i;
117         uint32_t pkt_size;
118         char req[1024] = { 0 };
119
120         for (i = 0; i < sizeof(req); i++) {
121                 req[i] = i % CHAR_MAX;
122         }
123
124         test_setup(test2_callback, &fd, &ctdb, NULL);
125
126         /*
127          * request 0
128          */
129
130         pkt_size = sizeof(uint32_t) + test2_req_len[0];
131         ret = write(fd, &pkt_size, sizeof(pkt_size));
132         assert(ret == sizeof(pkt_size));
133
134         ret = write(fd, req, test2_req_len[0]);
135         assert(ret == test2_req_len[0]);
136
137         /*
138          * request 1
139          */
140         pkt_size = sizeof(uint32_t) + test2_req_len[1];
141         ret = write(fd, &pkt_size, sizeof(pkt_size));
142         assert(ret == sizeof(pkt_size));
143
144         /*
145          * Omit the last byte to avoid buffer processing.
146          */
147         ret = write(fd, req, test2_req_len[1] - 1);
148         assert(ret == test2_req_len[1] - 1);
149
150         tevent_loop_once(ctdb->ev);
151
152         /*
153          * Write the missing byte now.
154          */
155         ret = write(fd, &req[test2_req_len[1] - 1], 1);
156         assert(ret == 1);
157
158         /*
159          * request 2
160          */
161         pkt_size = sizeof(uint32_t) + test2_req_len[2];
162         ret = write(fd, &pkt_size, sizeof(pkt_size));
163         assert(ret == sizeof(pkt_size));
164
165         ret = write(fd, req, test2_req_len[2]);
166         assert(ret == test2_req_len[2]);
167
168         tevent_loop_once(ctdb->ev);
169         tevent_loop_once(ctdb->ev);
170
171         assert(test2_cb_num == 2);
172
173         TALLOC_FREE(ctdb);
174 }
175
176 static void test_cb(uint8_t *data, size_t length, void *private_data)
177 {
178         /* dummy handler, not verifying anything */
179         TALLOC_FREE(data);
180 }
181
182 static void test3(void)
183 {
184         struct ctdb_context *ctdb;
185         struct ctdb_queue *queue;
186         uint32_t pkt_size;
187         char *request;
188         size_t req_len;
189         int fd;
190         int ret;
191
192         test_setup(test_cb, &fd, &ctdb, &queue);
193         request = talloc_zero_size(queue, queue->buffer_size);
194
195         /*
196          * calculate a request length which will fit into the buffer
197          * but not twice. Because we need to write the size integer
198          * as well (4-bytes) we're guaranteed that no 2 packets will fit.
199          */
200         req_len = queue->buffer_size >> 1;
201
202         /* writing first packet */
203         pkt_size = sizeof(uint32_t) + req_len;
204
205         ret = write(fd, &pkt_size, sizeof(pkt_size));
206         assert(ret == sizeof(pkt_size));
207
208         ret = write(fd, request, req_len);
209         assert(ret == req_len);
210
211         /* writing second, incomplete packet */
212         pkt_size = sizeof(uint32_t) + req_len;
213
214         ret = write(fd, &pkt_size, sizeof(pkt_size));
215         assert(ret == sizeof(pkt_size));
216
217         ret = write(fd, request, req_len >> 1);
218         assert(ret == req_len >> 1);
219
220         /* process...only 1st packet can be processed */
221         tevent_loop_once(ctdb->ev);
222
223         /* we should see a progressed offset of req_len + sizeof(pkt_size) */
224         assert(queue->buffer.offset == req_len + sizeof(pkt_size));
225
226         /* writing another few bytes of the still incomplete packet */
227         ret = write(fd, request, (req_len >> 1) - 1);
228         assert(ret == (req_len >> 1) - 1);
229
230         /*
231          * the packet is still incomplete and connot be processed
232          * but the packet data had to be moved in the buffer in order
233          * to fetch the new 199 bytes -> offset must be 0 now.
234          */
235         tevent_loop_once(ctdb->ev);
236         /*
237          * needs to be called twice as an incomplete packet
238          * does not trigger a schedule_immediate
239          */
240         tevent_loop_once(ctdb->ev);
241
242         assert(queue->buffer.offset == 0);
243
244         TALLOC_FREE(ctdb);
245 }
246
247 static void test4(void)
248 {
249         struct ctdb_context *ctdb;
250         struct ctdb_queue *queue;
251         uint32_t pkt_size;
252         char *request;
253         size_t req_len;
254         int fd;
255         int ret;
256
257         test_setup(test_cb, &fd, &ctdb, &queue);
258
259         req_len = queue->buffer_size << 1; /* double the buffer size */
260         request = talloc_zero_size(queue, req_len);
261
262         /* writing first part of packet exceeding standard buffer size */
263         pkt_size = sizeof(uint32_t) + req_len;
264
265         ret = write(fd, &pkt_size, sizeof(pkt_size));
266         assert(ret == sizeof(pkt_size));
267
268         ret = write(fd, request, req_len - (queue->buffer_size >> 1));
269         assert(ret == req_len - (queue->buffer_size >> 1));
270
271         /*
272          * process...
273          * this needs to be done to have things changed
274          */
275         tevent_loop_once(ctdb->ev);
276         /*
277          * needs to be called twice as an initial incomplete packet
278          * does not trigger a schedule_immediate
279          */
280         tevent_loop_once(ctdb->ev);
281
282         /* the buffer should be resized to packet size now */
283         assert(queue->buffer.size == pkt_size);
284
285         /* writing remaining data */
286         ret = write(fd, request, queue->buffer_size >> 1);
287         assert(ret == (queue->buffer_size >> 1));
288
289         /* process... */
290         tevent_loop_once(ctdb->ev);
291
292         /*
293          * the buffer was increased beyond its standard size.
294          * once packet got processed, the buffer has to be free'd
295          * and will be re-allocated with standard size on new request arrival.
296          */
297
298         assert(queue->buffer.size == 0);
299
300         /* writing new packet to verify standard buffer size */
301         pkt_size = sizeof(uint32_t) + (queue->buffer_size >> 1);
302
303         ret = write(fd, &pkt_size, sizeof(pkt_size));
304         assert(ret == sizeof(pkt_size));
305
306         ret = write(fd, request, (queue->buffer_size >> 1));
307         assert(ret == (queue->buffer_size >> 1));
308
309         /* process... */
310         tevent_loop_once(ctdb->ev);
311
312         /* back to standard buffer size */
313         assert(queue->buffer.size == queue->buffer_size);
314
315         TALLOC_FREE(ctdb);
316 }
317
318 int main(int argc, const char **argv)
319 {
320         int num;
321
322         if (argc != 2) {
323                 fprintf(stderr, "%s <testnum>\n", argv[0]);
324                 exit(1);
325         }
326
327
328         num = atoi(argv[1]);
329         switch (num) {
330         case 1:
331                 test1();
332                 break;
333
334         case 2:
335                 test2();
336                 break;
337
338         case 3:
339                 test3();
340                 break;
341
342         case 4:
343                 test4();
344                 break;
345
346         default:
347                 fprintf(stderr, "Unknown test number %s\n", argv[1]);
348         }
349
350         return 0;
351 }