1 /*
2 * SysDB - t/unit/utils/dbi_test.c
3 * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 #if HAVE_CONFIG_H
29 # include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "testutils.h"
33 #include "utils/dbi.h"
35 #include <check.h>
36 #include <dbi/dbi.h>
38 #define TEST_MAGIC ((void *)0x1337)
40 /*
41 * private data-types
42 */
43 typedef union {
44 long long integer;
45 double decimal;
46 const char *string;
47 time_t datetime;
48 struct {
49 size_t length;
50 const unsigned char *datum;
51 } binary;
52 } mock_data_t;
54 typedef struct {
55 const char *name;
56 unsigned long long nrows;
57 unsigned long long current_row;
58 unsigned int nfields;
59 unsigned short *field_types;
60 char **field_names;
61 } mock_query_t;
63 /*
64 * private variables
65 */
67 static sdb_dbi_client_t *client;
69 /*
70 * mock queries
71 */
73 /* field definitions */
74 static unsigned short field_types[] = {
75 DBI_TYPE_INTEGER,
76 DBI_TYPE_DECIMAL,
77 DBI_TYPE_STRING,
78 DBI_TYPE_DATETIME,
79 DBI_TYPE_BINARY,
80 };
81 static char *field_names[] = {
82 "field0",
83 "field1",
84 "field2",
85 "field3",
86 "field4",
87 };
89 #define DATUM(p) ((const unsigned char *)(p))
90 static mock_data_t golden_data[][5] = {
91 {
92 { .integer = 1234 },
93 { .decimal = 1.234 },
94 { .string = "abcd" },
95 { .datetime = 0 },
96 { .binary = { 1, DATUM("a") } },
97 },
98 {
99 { .integer = 2345 },
100 { .decimal = 23.45 },
101 { .string = "bcde" },
102 { .datetime = 1 },
103 { .binary = { 4, DATUM("bcde") } },
104 },
105 {
106 { .integer = 3456 },
107 { .decimal = 345.6 },
108 { .string = "cd" },
109 { .datetime = 2 },
110 { .binary = { 0, DATUM(NULL) } },
111 },
112 {
113 { .integer = 4567 },
114 { .decimal = 4567 },
115 { .string = "d" },
116 { .datetime = 3 },
117 { .binary = { 13, DATUM("defghijklmnop") } },
118 },
119 {
120 { .integer = 5678 },
121 { .decimal = 56.78 },
122 { .string = "efgh" },
123 { .datetime = 4 },
124 { .binary = { 5, DATUM("efghi") } },
125 },
126 };
128 /* query definitions */
129 static mock_query_t mock_queries[] = {
130 { "mockquery0", 5, 1, 0, NULL, NULL },
131 { "mockquery1", 0, 0, 1, field_types, field_names },
132 { "mockquery2", 1, 1, 1, field_types, field_names },
133 { "mockquery3", 2, 1, 1, field_types, field_names },
134 { "mockquery4", 5, 1, 1, field_types, field_names },
135 { "mockquery5", 5, 1, 2, field_types, field_names },
136 { "mockquery6", 5, 1, 3, field_types, field_names },
137 { "mockquery7", 5, 1, 4, field_types, field_names },
138 { "mockquery8", 5, 1, 5, field_types, field_names },
139 };
141 static mock_query_t *current_query = NULL;
143 /*
144 * mocked functions
145 */
147 /* dbi_driver, dbi_conn, dbi_result are void pointers */
149 #if LIBDBI_VERSION < 900
150 typedef void *dbi_inst;
152 int
153 dbi_initialize_r(const char *driverdir, dbi_inst *pInst);
154 void
155 dbi_shutdown_r(dbi_inst inst);
156 dbi_driver
157 dbi_driver_open_r(const char *name, dbi_inst inst);
158 dbi_driver
159 dbi_driver_list_r(dbi_driver curr, dbi_inst inst);
160 #endif
162 const dbi_inst INST = (void *)0x4711;
164 int
165 dbi_initialize_r(const char __attribute__((unused)) *driverdir, dbi_inst *pInst)
166 {
167 *pInst = INST;
168 return 0;
169 } /* dbi_initialize_r */
171 void
172 dbi_shutdown_r(dbi_inst inst)
173 {
174 fail_unless(inst == INST,
175 "dbi_shutdown_r() called with unexpected inst=%p; expected %p",
176 inst, INST);
177 } /* dbi_shutdown_r */
179 dbi_driver
180 dbi_driver_open_r(const char *name, dbi_inst inst)
181 {
182 fail_unless(inst == INST,
183 "dbi_driver_open_r() called with unexpected inst=%p; expected %p",
184 inst, INST);
186 if (strcmp(name, "mockdriver"))
187 return NULL;
188 return (dbi_driver)"mockdriver";
189 } /* dbi_driver_open */
191 dbi_driver
192 dbi_driver_list_r(dbi_driver curr, dbi_inst inst)
193 {
194 fail_unless(inst == INST,
195 "dbi_driver_list_r() called with unexpected inst=%p; expected %p",
196 inst, INST);
198 if (!curr)
199 return "mockdriver";
200 return NULL;
201 } /* dbi_driver_list */
203 #if LIBDBI_VERSION < 900
204 int
205 dbi_initialize(const char *driverdir)
206 {
207 return dbi_initialize_r(driverdir, NULL);
208 } /* dbi_initialize */
210 /* for some weird reason, gcc and clang complain about a missing prototype for
211 * dbi_shutdown; however, the function is declared in dbi/dbi.h */
212 void
213 dbi_shutdown(void);
214 void
215 dbi_shutdown(void)
216 {
217 dbi_shutdown_r(NULL);
218 } /* dbi_shutdown */
220 dbi_driver
221 dbi_driver_open(const char *name)
222 {
223 return dbi_driver_open_r(name, NULL);
224 } /* dbi_driver_open */
226 dbi_driver
227 dbi_driver_list(dbi_driver curr)
228 {
229 return dbi_driver_list_r(curr, NULL);
230 } /* dbi_driver_list */
231 #endif
233 const char *
234 dbi_driver_get_name(dbi_driver driver)
235 {
236 return (const char *)driver;
237 } /* dbi_driver_get_name */
239 int
240 dbi_conn_set_option(dbi_conn __attribute__((unused)) conn,
241 const char __attribute__((unused)) *key,
242 const char __attribute__((unused)) *value)
243 {
244 return 0;
245 } /* dbi_conn_set_option */
247 const char *
248 dbi_conn_get_option_list(dbi_conn __attribute__((unused)) conn,
249 const char __attribute__((unused)) *key)
250 {
251 return NULL;
252 } /* dbi_conn_get_option_list */
254 dbi_conn
255 dbi_conn_open(dbi_driver driver)
256 {
257 if (strcmp((const char *)driver, "mockdriver"))
258 return NULL;
259 return (dbi_conn)"mockconnection";
260 } /* dbi_conn_open */
262 static unsigned long long dbi_conn_connect_called = 0;
263 int
264 dbi_conn_connect(dbi_conn conn)
265 {
266 ++dbi_conn_connect_called;
267 if (strcmp((const char *)conn, "mockconnection"))
268 return DBI_ERROR_NOCONN;
269 return 0;
270 } /* dbi_conn_connect */
272 int
273 dbi_conn_ping(dbi_conn conn)
274 {
275 if (strcmp((const char *)conn, "mockconnection"))
276 return 0;
277 return 1;
278 } /* dbi_conn_connect */
280 void
281 dbi_conn_close(dbi_conn __attribute__((unused)) conn)
282 {
283 return;
284 } /* dbi_conn_close */
286 int
287 dbi_conn_error(dbi_conn conn, const char **errmsg)
288 {
289 if ((! conn) || (strcmp((const char *)conn, "mockconnection")))
290 return DBI_ERROR_BADOBJECT;
291 if (errmsg)
292 *errmsg = "mockerror";
293 return DBI_ERROR_UNSUPPORTED;
294 } /* dbi_conn_error */
296 static unsigned long long dbi_conn_query_called = 0;
297 dbi_result
298 dbi_conn_query(dbi_conn conn, const char __attribute__((unused)) *statement)
299 {
300 size_t i;
302 ++dbi_conn_query_called;
303 if (strcmp((const char *)conn, "mockconnection"))
304 return NULL;
306 for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
307 if (!strcmp(mock_queries[i].name, statement)) {
308 current_query = &mock_queries[i];
309 return (dbi_result)current_query;
310 }
311 }
312 return NULL;
313 } /* dbi_conn_query */
315 unsigned long long
316 dbi_result_get_numrows(dbi_result res)
317 {
318 mock_query_t *q = res;
319 if (! q)
320 return DBI_ROW_ERROR;
321 return q->nrows;
322 } /* dbi_result_get_numrows */
324 unsigned int
325 dbi_result_get_numfields(dbi_result res)
326 {
327 mock_query_t *q = res;
328 if (! q)
329 return DBI_FIELD_ERROR;
330 return q->nfields;
331 } /* dbi_result_get_numfields */
333 unsigned short
334 dbi_result_get_field_type_idx(dbi_result res, unsigned int i)
335 {
336 mock_query_t *q = res;
337 if ((! q) || (i > q->nfields))
338 return DBI_TYPE_ERROR;
339 return q->field_types[i - 1];
340 } /* dbi_result_get_field_type_idx */
342 const char *
343 dbi_result_get_field_name(dbi_result res, unsigned int i)
344 {
345 mock_query_t *q = res;
346 if ((! q) || (i > q->nfields))
347 return NULL;
348 return q->field_names[i - 1];
349 } /* dbi_result_get_field_name */
351 int
352 dbi_result_seek_row(dbi_result res, unsigned long long n)
353 {
354 mock_query_t *q = res;
355 if ((! q) || (n > q->nrows))
356 return 0;
358 q->current_row = n;
359 return 1;
360 } /* dbi_result_seek_row */
362 static mock_data_t
363 get_golden_data(dbi_result res, unsigned int i) {
364 mock_query_t *q = res;
365 fail_unless(q != NULL, "dbi_result_get_*_idx() called with "
366 "NULL result data; expected valid result object");
368 /* this information is managed by seek_row and, thus,
369 * should never be invalid */
370 fail_unless(q->current_row && q->current_row <= q->nrows,
371 "INTERNAL ERROR: current row out of range");
373 fail_unless(i && i <= q->nfields,
374 "dbi_result_get_*_idx() called with index out of range; "
375 "got: %u; expected [1, %u]", i, q->nfields);
376 return golden_data[q->current_row - 1][i - 1];
377 } /* get_golden_data */
379 long long
380 dbi_result_get_longlong_idx(dbi_result res, unsigned int i)
381 {
382 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_INTEGER,
383 "dbi_result_get_longlong_idx() called for non-integer "
384 "column type %u", current_query->field_types[i - 1]);
385 return get_golden_data(res, i).integer;
386 } /* dbi_result_get_longlong_idx */
388 double
389 dbi_result_get_double_idx(dbi_result res, unsigned int i)
390 {
391 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_DECIMAL,
392 "dbi_result_get_double_idx() called for non-decimal "
393 "column type %u", current_query->field_types[i - 1]);
394 return get_golden_data(res, i).decimal;
395 } /* dbi_result_get_double_idx */
397 const char *
398 dbi_result_get_string_idx(dbi_result res, unsigned int i)
399 {
400 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_STRING,
401 "dbi_result_get_string_idx() called for non-string "
402 "column type %u", current_query->field_types[i - 1]);
403 return get_golden_data(res, i).string;
404 } /* dbi_result_get_string_idx */
406 char *
407 dbi_result_get_string_copy_idx(dbi_result res, unsigned int i)
408 {
409 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_STRING,
410 "dbi_result_get_string_copy_idx() called for non-string "
411 "column type %u", current_query->field_types[i - 1]);
412 if (! get_golden_data(res, i).string)
413 return NULL;
414 return strdup(get_golden_data(res, i).string);
415 } /* dbi_result_get_string_copy_idx */
417 time_t
418 dbi_result_get_datetime_idx(dbi_result res, unsigned int i)
419 {
420 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_DATETIME,
421 "dbi_result_get_datetime_idx() called for non-datetime "
422 "column type %u", current_query->field_types[i - 1]);
423 return get_golden_data(res, i).datetime;
424 } /* dbi_result_get_datetime_idx */
426 size_t
427 dbi_result_get_field_length_idx(dbi_result res, unsigned int i)
428 {
429 /* this will check if the parameters are valid */
430 get_golden_data(res, i);
432 switch (current_query->field_types[i - 1]) {
433 case DBI_TYPE_INTEGER:
434 return sizeof(long long);
435 break;
436 case DBI_TYPE_DECIMAL:
437 return sizeof(double);
438 break;
439 case DBI_TYPE_STRING:
440 return strlen(get_golden_data(res, i).string) + 1;
441 break;
442 case DBI_TYPE_DATETIME:
443 return sizeof(time_t);
444 break;
445 case DBI_TYPE_BINARY:
446 return get_golden_data(res, i).binary.length;
447 break;
448 }
450 fail("INTERNAL ERROR: dbi_result_get_field_length_idx() "
451 "called for unexpected field type %u",
452 current_query->field_types[i - 1]);
453 return 0;
454 } /* dbi_result_get_field_length_idx */
456 const unsigned char *
457 dbi_result_get_binary_idx(dbi_result res, unsigned int i)
458 {
459 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_BINARY,
460 "dbi_result_get_binary_idx() called for non-binary "
461 "column type %u", current_query->field_types[i - 1]);
462 return get_golden_data(res, i).binary.datum;
463 } /* dbi_result_get_binary_idx */
465 unsigned char *
466 dbi_result_get_binary_copy_idx(dbi_result res, unsigned int i)
467 {
468 const char *data;
469 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_BINARY,
470 "dbi_result_get_binary_copy_idx() called for non-binary "
471 "column type %u", current_query->field_types[i - 1]);
472 data = (const char *)get_golden_data(res, i).binary.datum;
473 if (! data)
474 return NULL;
475 return (unsigned char *)strdup(data);
476 } /* dbi_result_get_binary_copy_idx */
478 static unsigned long long dbi_result_free_called = 0;
479 int
480 dbi_result_free(dbi_result res)
481 {
482 mock_query_t *q = res;
484 ++dbi_result_free_called;
485 if (! q)
486 return -1;
488 current_query = NULL;
489 return 0;
490 } /* dbi_result_free */
492 /*
493 * private helper functions
494 */
496 static void
497 setup(void)
498 {
499 client = sdb_dbi_client_create("mockdriver", "mockdatabase");
500 fail_unless(client != NULL,
501 "sdb_dbi_client_create() = NULL; expected client object");
503 dbi_conn_connect_called = 0;
504 } /* setup */
506 static void
507 connect(void)
508 {
509 int check = sdb_dbi_client_connect(client);
510 fail_unless(check == 0,
511 "sdb_dbi_client_connect() = %i; expected: 0", check);
512 } /* connect */
514 static void
515 teardown(void)
516 {
517 sdb_dbi_client_destroy(client);
518 client = NULL;
519 } /* teardown */
521 static unsigned long long query_callback_called = 0;
522 static int
523 query_callback(sdb_dbi_client_t *c,
524 size_t n, sdb_data_t *data, sdb_object_t *user_data)
525 {
526 size_t i;
528 ++query_callback_called;
530 fail_unless(c == client,
531 "query callback received unexpected client = %p; "
532 "expected: %p", c, client);
533 fail_unless(n == current_query->nfields,
534 "query callback received n = %i; expected: %i",
535 n, current_query->nfields);
536 fail_unless(data != NULL,
537 "query callback received data = NULL; expected: valid data");
538 fail_unless(user_data == TEST_MAGIC,
539 "query callback received user_data = %p; expected: %p",
540 user_data, TEST_MAGIC);
542 for (i = 0; i < n; ++i) {
543 int expected_type = DBI_TYPE_TO_SDB(current_query->field_types[i]);
544 mock_data_t expected_data;
546 fail_unless((int)data[i].type == expected_type,
547 "query callback received unexpected type %i for "
548 "column %zu; expected: %i", data[i].type, i,
549 expected_type);
551 expected_data = golden_data[current_query->current_row - 1][i];
552 switch (expected_type) {
553 case SDB_TYPE_INTEGER:
554 fail_unless(data[i].data.integer == expected_data.integer,
555 "query callback received unexpected data %lli "
556 "for column %zu; expected: %lli",
557 data[i].data.integer, i, expected_data.integer);
558 break;
559 case SDB_TYPE_DECIMAL:
560 fail_unless(data[i].data.decimal == expected_data.decimal,
561 "query callback received unexpected data %g "
562 "for column %zu; expected: %g",
563 data[i].data.decimal, i, expected_data.decimal);
564 break;
565 case SDB_TYPE_STRING:
566 fail_unless(!strcmp(data[i].data.string, expected_data.string),
567 "query callback received unexpected data %s "
568 "for column %zu; expected: %s",
569 data[i].data.string, i, expected_data.string);
570 break;
571 case SDB_TYPE_DATETIME:
572 fail_unless(data[i].data.datetime
573 == SECS_TO_SDB_TIME(expected_data.datetime),
574 "query callback received unexpected data "PRIsdbTIME
575 " for column %zu; expected: "PRIsdbTIME,
576 data[i].data.integer, i,
577 SECS_TO_SDB_TIME(expected_data.integer));
578 break;
579 case SDB_TYPE_BINARY:
580 fail_unless(data[i].data.binary.length ==
581 expected_data.binary.length,
582 "query callback received unexpected "
583 "binary data length %zu for column %zu; "
584 "expected: %lli", data[i].data.binary.length, i,
585 expected_data.binary.length);
586 fail_unless(!memcmp(data[i].data.binary.datum,
587 expected_data.binary.datum,
588 expected_data.binary.length),
589 "query callback received unexpected binary data %p "
590 "for column %zu; expected: %p",
591 data[i].data.binary.datum, i,
592 expected_data.binary.datum);
593 break;
594 default:
595 fail("INTERNAL ERROR: query callback received "
596 "unknown type %i for column %zu",
597 expected_type, i);
598 }
599 }
600 return 0;
601 } /* query_callback */
603 /*
604 * tests
605 */
607 START_TEST(test_sdb_dbi_client_connect)
608 {
609 sdb_dbi_options_t *opts;
610 int check;
612 check = sdb_dbi_client_connect(client);
613 fail_unless(check == 0,
614 "sdb_dbi_client_connect() = %i; expected: 0", check);
615 fail_unless(dbi_conn_connect_called == 1,
616 "sdb_dbi_client_create() called dbi_conn_connect %i times; "
617 "expected: 1", dbi_conn_connect_called);
619 /* calling it again shall reconnect */
620 check = sdb_dbi_client_connect(client);
621 fail_unless(check == 0,
622 "repeated sdb_dbi_client_connect() = %i; expected: 0", check);
623 fail_unless(dbi_conn_connect_called == 2,
624 "repeated sdb_dbi_client_create() called dbi_conn_connect %i times; "
625 "expected: 2", dbi_conn_connect_called);
627 opts = sdb_dbi_options_create();
628 fail_unless(opts != NULL,
629 "sdb_dbi_options_create() returned NULL; expected <opts>");
630 check = sdb_dbi_options_add(opts, "a", "1");
631 fail_unless(check == 0,
632 "sdb_dbi_options_add('a', '1') = %d; expected: 0", check);
633 check = sdb_dbi_options_add(opts, "b", "2");
634 fail_unless(check == 0,
635 "sdb_dbi_options_add('b', '2') = %d; expected: 0", check);
637 check = sdb_dbi_client_set_options(client, opts);
638 fail_unless(check == 0,
639 "sdb_dbi_client_set_options() = %d; expected: 0", check);
640 /* reconnect with options */
641 check = sdb_dbi_client_connect(client);
642 fail_unless(check == 0,
643 "repeated, with options sdb_dbi_client_connect() = %i; expected: 0",
644 check);
645 fail_unless(dbi_conn_connect_called == 3,
646 "repeated, with options sdb_dbi_client_create() called "
647 "dbi_conn_connect %i times; expected: 3", dbi_conn_connect_called);
649 /* The client takes ownership of the options, so no need to destroy it. */
650 }
651 END_TEST
653 START_TEST(test_sdb_dbi_client_check_conn)
654 {
655 int check = sdb_dbi_client_check_conn(client);
656 fail_unless(check == 0,
657 "sdb_dbi_client_check_conn() = %i; expected: 0", check);
659 /* the first call will actually connect to the database */
660 fail_unless(dbi_conn_connect_called == 1,
661 "sdb_dbi_client_check_conn() called dbi_conn_connect %i times; "
662 "expected: 1", dbi_conn_connect_called);
664 dbi_conn_connect_called = 0;
665 check = sdb_dbi_client_check_conn(client);
666 fail_unless(check == 0,
667 "sdb_dbi_client_check_conn() = %i; expected: 0", check);
669 /* should not reconnect */
670 fail_unless(dbi_conn_connect_called == 0,
671 "sdb_dbi_client_check_conn() called dbi_conn_connect %i time(s); "
672 "expected: 0", dbi_conn_connect_called);
673 }
674 END_TEST
676 START_TEST(test_sdb_dbi_exec_query)
677 {
678 size_t i;
680 int check = sdb_dbi_exec_query(client, "mockquery0", query_callback,
681 /* user_data = */ TEST_MAGIC, /* n = */ 0);
682 /* not connected yet */
683 fail_unless(check < 0,
684 "sdb_dbi_exec_query() = %i; expected: < 0", check);
686 connect();
688 for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
689 mock_query_t *q = &mock_queries[i];
691 unsigned long long expected_callback_calls = 0;
693 dbi_conn_query_called = 0;
694 query_callback_called = 0;
695 dbi_result_free_called = 0;
697 /* sdb_dbi_exec_query will only use as many type arguments are needed,
698 * so we can safely pass in the maximum number of arguments required
699 * on each call */
700 check = sdb_dbi_exec_query(client, q->name, query_callback,
701 /* user_data = */ TEST_MAGIC, /* n = */ (int)q->nfields,
702 SDB_TYPE_INTEGER, SDB_TYPE_DECIMAL, SDB_TYPE_STRING,
703 SDB_TYPE_DATETIME, SDB_TYPE_BINARY);
704 fail_unless(check == 0,
705 "sdb_dbi_exec_query() = %i; expected: 0", check);
707 fail_unless(dbi_conn_query_called == 1,
708 "sdb_dbi_exec_query() called dbi_conn_query %i times; "
709 "expected: 1", dbi_conn_query_called);
711 if (q->nfields)
712 expected_callback_calls = q->nrows;
714 fail_unless(query_callback_called == expected_callback_calls,
715 "sdb_dbi_exec_query() did not call the registered callback "
716 "for each result row; got %i call%s; expected: 0",
717 query_callback_called,
718 (query_callback_called == 1) ? "" : "s");
720 fail_unless(dbi_result_free_called == 1,
721 "sdb_dbi_exec_query() did not free the query result object");
722 }
723 }
724 END_TEST
726 TEST_MAIN("utils::dbi")
727 {
728 TCase *tc = tcase_create("core");
729 tcase_add_checked_fixture(tc, setup, teardown);
730 tcase_add_test(tc, test_sdb_dbi_client_connect);
731 tcase_add_test(tc, test_sdb_dbi_client_check_conn);
732 tcase_add_test(tc, test_sdb_dbi_exec_query);
733 ADD_TCASE(tc);
734 }
735 TEST_MAIN_END
737 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */