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