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 if (pInst)
168 *pInst = INST;
169 return 0;
170 } /* dbi_initialize_r */
172 void
173 dbi_shutdown_r(dbi_inst inst)
174 {
175 fail_unless(inst == INST,
176 "dbi_shutdown_r() called with unexpected inst=%p; expected %p",
177 inst, INST);
178 } /* dbi_shutdown_r */
180 dbi_driver
181 dbi_driver_open_r(const char *name, dbi_inst inst)
182 {
183 fail_unless(inst == INST,
184 "dbi_driver_open_r() called with unexpected inst=%p; expected %p",
185 inst, INST);
187 if (strcmp(name, "mockdriver"))
188 return NULL;
189 return (dbi_driver)"mockdriver";
190 } /* dbi_driver_open */
192 dbi_driver
193 dbi_driver_list_r(dbi_driver curr, dbi_inst inst)
194 {
195 fail_unless(inst == INST,
196 "dbi_driver_list_r() called with unexpected inst=%p; expected %p",
197 inst, INST);
199 if (!curr)
200 return "mockdriver";
201 return NULL;
202 } /* dbi_driver_list */
204 #if LIBDBI_VERSION < 900
205 int
206 dbi_initialize(const char *driverdir)
207 {
208 return dbi_initialize_r(driverdir, NULL);
209 } /* dbi_initialize */
211 /* for some weird reason, gcc and clang complain about a missing prototype for
212 * dbi_shutdown; however, the function is declared in dbi/dbi.h */
213 void
214 dbi_shutdown(void);
215 void
216 dbi_shutdown(void)
217 {
218 dbi_shutdown_r(INST);
219 } /* dbi_shutdown */
221 dbi_driver
222 dbi_driver_open(const char *name)
223 {
224 return dbi_driver_open_r(name, INST);
225 } /* dbi_driver_open */
227 dbi_driver
228 dbi_driver_list(dbi_driver curr)
229 {
230 return dbi_driver_list_r(curr, INST);
231 } /* dbi_driver_list */
232 #endif
234 const char *
235 dbi_driver_get_name(dbi_driver driver)
236 {
237 return (const char *)driver;
238 } /* dbi_driver_get_name */
240 int
241 dbi_conn_set_option(dbi_conn __attribute__((unused)) conn,
242 const char __attribute__((unused)) *key,
243 const char __attribute__((unused)) *value)
244 {
245 return 0;
246 } /* dbi_conn_set_option */
248 const char *
249 dbi_conn_get_option_list(dbi_conn __attribute__((unused)) conn,
250 const char __attribute__((unused)) *key)
251 {
252 return NULL;
253 } /* dbi_conn_get_option_list */
255 dbi_conn
256 dbi_conn_open(dbi_driver driver)
257 {
258 if (strcmp((const char *)driver, "mockdriver"))
259 return NULL;
260 return (dbi_conn)"mockconnection";
261 } /* dbi_conn_open */
263 static unsigned long long dbi_conn_connect_called = 0;
264 int
265 dbi_conn_connect(dbi_conn conn)
266 {
267 ++dbi_conn_connect_called;
268 if (strcmp((const char *)conn, "mockconnection"))
269 return DBI_ERROR_NOCONN;
270 return 0;
271 } /* dbi_conn_connect */
273 int
274 dbi_conn_ping(dbi_conn conn)
275 {
276 if (strcmp((const char *)conn, "mockconnection"))
277 return 0;
278 return 1;
279 } /* dbi_conn_connect */
281 void
282 dbi_conn_close(dbi_conn __attribute__((unused)) conn)
283 {
284 return;
285 } /* dbi_conn_close */
287 int
288 dbi_conn_error(dbi_conn conn, const char **errmsg)
289 {
290 if ((! conn) || (strcmp((const char *)conn, "mockconnection")))
291 return DBI_ERROR_BADOBJECT;
292 if (errmsg)
293 *errmsg = "mockerror";
294 return DBI_ERROR_UNSUPPORTED;
295 } /* dbi_conn_error */
297 static unsigned long long dbi_conn_query_called = 0;
298 dbi_result
299 dbi_conn_query(dbi_conn conn, const char __attribute__((unused)) *statement)
300 {
301 size_t i;
303 ++dbi_conn_query_called;
304 if (strcmp((const char *)conn, "mockconnection"))
305 return NULL;
307 for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
308 if (!strcmp(mock_queries[i].name, statement)) {
309 current_query = &mock_queries[i];
310 return (dbi_result)current_query;
311 }
312 }
313 return NULL;
314 } /* dbi_conn_query */
316 unsigned long long
317 dbi_result_get_numrows(dbi_result res)
318 {
319 mock_query_t *q = res;
320 if (! q)
321 return DBI_ROW_ERROR;
322 return q->nrows;
323 } /* dbi_result_get_numrows */
325 unsigned int
326 dbi_result_get_numfields(dbi_result res)
327 {
328 mock_query_t *q = res;
329 if (! q)
330 return DBI_FIELD_ERROR;
331 return q->nfields;
332 } /* dbi_result_get_numfields */
334 unsigned short
335 dbi_result_get_field_type_idx(dbi_result res, unsigned int i)
336 {
337 mock_query_t *q = res;
338 if ((! q) || (i > q->nfields))
339 return DBI_TYPE_ERROR;
340 return q->field_types[i - 1];
341 } /* dbi_result_get_field_type_idx */
343 const char *
344 dbi_result_get_field_name(dbi_result res, unsigned int i)
345 {
346 mock_query_t *q = res;
347 if ((! q) || (i > q->nfields))
348 return NULL;
349 return q->field_names[i - 1];
350 } /* dbi_result_get_field_name */
352 int
353 dbi_result_seek_row(dbi_result res, unsigned long long n)
354 {
355 mock_query_t *q = res;
356 if ((! q) || (n > q->nrows))
357 return 0;
359 q->current_row = n;
360 return 1;
361 } /* dbi_result_seek_row */
363 static mock_data_t
364 get_golden_data(dbi_result res, unsigned int i) {
365 mock_query_t *q = res;
366 fail_unless(q != NULL, "dbi_result_get_*_idx() called with "
367 "NULL result data; expected valid result object");
369 /* this information is managed by seek_row and, thus,
370 * should never be invalid */
371 fail_unless(q->current_row && q->current_row <= q->nrows,
372 "INTERNAL ERROR: current row out of range");
374 fail_unless(i && i <= q->nfields,
375 "dbi_result_get_*_idx() called with index out of range; "
376 "got: %u; expected [1, %u]", i, q->nfields);
377 return golden_data[q->current_row - 1][i - 1];
378 } /* get_golden_data */
380 long long
381 dbi_result_get_longlong_idx(dbi_result res, unsigned int i)
382 {
383 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_INTEGER,
384 "dbi_result_get_longlong_idx() called for non-integer "
385 "column type %u", current_query->field_types[i - 1]);
386 return get_golden_data(res, i).integer;
387 } /* dbi_result_get_longlong_idx */
389 double
390 dbi_result_get_double_idx(dbi_result res, unsigned int i)
391 {
392 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_DECIMAL,
393 "dbi_result_get_double_idx() called for non-decimal "
394 "column type %u", current_query->field_types[i - 1]);
395 return get_golden_data(res, i).decimal;
396 } /* dbi_result_get_double_idx */
398 const char *
399 dbi_result_get_string_idx(dbi_result res, unsigned int i)
400 {
401 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_STRING,
402 "dbi_result_get_string_idx() called for non-string "
403 "column type %u", current_query->field_types[i - 1]);
404 return get_golden_data(res, i).string;
405 } /* dbi_result_get_string_idx */
407 char *
408 dbi_result_get_string_copy_idx(dbi_result res, unsigned int i)
409 {
410 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_STRING,
411 "dbi_result_get_string_copy_idx() called for non-string "
412 "column type %u", current_query->field_types[i - 1]);
413 if (! get_golden_data(res, i).string)
414 return NULL;
415 return strdup(get_golden_data(res, i).string);
416 } /* dbi_result_get_string_copy_idx */
418 time_t
419 dbi_result_get_datetime_idx(dbi_result res, unsigned int i)
420 {
421 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_DATETIME,
422 "dbi_result_get_datetime_idx() called for non-datetime "
423 "column type %u", current_query->field_types[i - 1]);
424 return get_golden_data(res, i).datetime;
425 } /* dbi_result_get_datetime_idx */
427 size_t
428 dbi_result_get_field_length_idx(dbi_result res, unsigned int i)
429 {
430 /* this will check if the parameters are valid */
431 get_golden_data(res, i);
433 switch (current_query->field_types[i - 1]) {
434 case DBI_TYPE_INTEGER:
435 return sizeof(long long);
436 break;
437 case DBI_TYPE_DECIMAL:
438 return sizeof(double);
439 break;
440 case DBI_TYPE_STRING:
441 return strlen(get_golden_data(res, i).string) + 1;
442 break;
443 case DBI_TYPE_DATETIME:
444 return sizeof(time_t);
445 break;
446 case DBI_TYPE_BINARY:
447 return get_golden_data(res, i).binary.length;
448 break;
449 }
451 fail("INTERNAL ERROR: dbi_result_get_field_length_idx() "
452 "called for unexpected field type %u",
453 current_query->field_types[i - 1]);
454 return 0;
455 } /* dbi_result_get_field_length_idx */
457 const unsigned char *
458 dbi_result_get_binary_idx(dbi_result res, unsigned int i)
459 {
460 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_BINARY,
461 "dbi_result_get_binary_idx() called for non-binary "
462 "column type %u", current_query->field_types[i - 1]);
463 return get_golden_data(res, i).binary.datum;
464 } /* dbi_result_get_binary_idx */
466 unsigned char *
467 dbi_result_get_binary_copy_idx(dbi_result res, unsigned int i)
468 {
469 const char *data;
470 fail_unless(current_query->field_types[i - 1] == DBI_TYPE_BINARY,
471 "dbi_result_get_binary_copy_idx() called for non-binary "
472 "column type %u", current_query->field_types[i - 1]);
473 data = (const char *)get_golden_data(res, i).binary.datum;
474 if (! data)
475 return NULL;
476 return (unsigned char *)strdup(data);
477 } /* dbi_result_get_binary_copy_idx */
479 static unsigned long long dbi_result_free_called = 0;
480 int
481 dbi_result_free(dbi_result res)
482 {
483 mock_query_t *q = res;
485 ++dbi_result_free_called;
486 if (! q)
487 return -1;
489 current_query = NULL;
490 return 0;
491 } /* dbi_result_free */
493 /*
494 * private helper functions
495 */
497 static void
498 setup(void)
499 {
500 client = sdb_dbi_client_create("mockdriver", "mockdatabase");
501 fail_unless(client != NULL,
502 "sdb_dbi_client_create() = NULL; expected client object");
504 dbi_conn_connect_called = 0;
505 } /* setup */
507 static void
508 connect(void)
509 {
510 int check = sdb_dbi_client_connect(client);
511 fail_unless(check == 0,
512 "sdb_dbi_client_connect() = %i; expected: 0", check);
513 } /* connect */
515 static void
516 teardown(void)
517 {
518 sdb_dbi_client_destroy(client);
519 client = NULL;
520 } /* teardown */
522 static unsigned long long query_callback_called = 0;
523 static int
524 query_callback(sdb_dbi_client_t *c,
525 size_t n, sdb_data_t *data, sdb_object_t *user_data)
526 {
527 size_t i;
529 ++query_callback_called;
531 fail_unless(c == client,
532 "query callback received unexpected client = %p; "
533 "expected: %p", c, client);
534 fail_unless(n == current_query->nfields,
535 "query callback received n = %i; expected: %i",
536 n, current_query->nfields);
537 fail_unless(data != NULL,
538 "query callback received data = NULL; expected: valid data");
539 fail_unless(user_data == TEST_MAGIC,
540 "query callback received user_data = %p; expected: %p",
541 user_data, TEST_MAGIC);
543 for (i = 0; i < n; ++i) {
544 int expected_type = DBI_TYPE_TO_SDB(current_query->field_types[i]);
545 mock_data_t expected_data;
547 fail_unless((int)data[i].type == expected_type,
548 "query callback received unexpected type %i for "
549 "column %zu; expected: %i", data[i].type, i,
550 expected_type);
552 expected_data = golden_data[current_query->current_row - 1][i];
553 switch (expected_type) {
554 case SDB_TYPE_INTEGER:
555 fail_unless(data[i].data.integer == expected_data.integer,
556 "query callback received unexpected data %lli "
557 "for column %zu; expected: %lli",
558 data[i].data.integer, i, expected_data.integer);
559 break;
560 case SDB_TYPE_DECIMAL:
561 fail_unless(data[i].data.decimal == expected_data.decimal,
562 "query callback received unexpected data %g "
563 "for column %zu; expected: %g",
564 data[i].data.decimal, i, expected_data.decimal);
565 break;
566 case SDB_TYPE_STRING:
567 fail_unless(!strcmp(data[i].data.string, expected_data.string),
568 "query callback received unexpected data %s "
569 "for column %zu; expected: %s",
570 data[i].data.string, i, expected_data.string);
571 break;
572 case SDB_TYPE_DATETIME:
573 fail_unless(data[i].data.datetime
574 == SECS_TO_SDB_TIME(expected_data.datetime),
575 "query callback received unexpected data "PRIsdbTIME
576 " for column %zu; expected: "PRIsdbTIME,
577 data[i].data.integer, i,
578 SECS_TO_SDB_TIME(expected_data.integer));
579 break;
580 case SDB_TYPE_BINARY:
581 fail_unless(data[i].data.binary.length ==
582 expected_data.binary.length,
583 "query callback received unexpected "
584 "binary data length %zu for column %zu; "
585 "expected: %lli", data[i].data.binary.length, i,
586 expected_data.binary.length);
587 fail_unless(!memcmp(data[i].data.binary.datum,
588 expected_data.binary.datum,
589 expected_data.binary.length),
590 "query callback received unexpected binary data %p "
591 "for column %zu; expected: %p",
592 data[i].data.binary.datum, i,
593 expected_data.binary.datum);
594 break;
595 default:
596 fail("INTERNAL ERROR: query callback received "
597 "unknown type %i for column %zu",
598 expected_type, i);
599 }
600 }
601 return 0;
602 } /* query_callback */
604 /*
605 * tests
606 */
608 START_TEST(test_sdb_dbi_client_connect)
609 {
610 sdb_dbi_options_t *opts;
611 int check;
613 check = sdb_dbi_client_connect(client);
614 fail_unless(check == 0,
615 "sdb_dbi_client_connect() = %i; expected: 0", check);
616 fail_unless(dbi_conn_connect_called == 1,
617 "sdb_dbi_client_create() called dbi_conn_connect %i times; "
618 "expected: 1", dbi_conn_connect_called);
620 /* calling it again shall reconnect */
621 check = sdb_dbi_client_connect(client);
622 fail_unless(check == 0,
623 "repeated sdb_dbi_client_connect() = %i; expected: 0", check);
624 fail_unless(dbi_conn_connect_called == 2,
625 "repeated sdb_dbi_client_create() called dbi_conn_connect %i times; "
626 "expected: 2", dbi_conn_connect_called);
628 opts = sdb_dbi_options_create();
629 fail_unless(opts != NULL,
630 "sdb_dbi_options_create() returned NULL; expected <opts>");
631 check = sdb_dbi_options_add(opts, "a", "1");
632 fail_unless(check == 0,
633 "sdb_dbi_options_add('a', '1') = %d; expected: 0", check);
634 check = sdb_dbi_options_add(opts, "b", "2");
635 fail_unless(check == 0,
636 "sdb_dbi_options_add('b', '2') = %d; expected: 0", check);
638 check = sdb_dbi_client_set_options(client, opts);
639 fail_unless(check == 0,
640 "sdb_dbi_client_set_options() = %d; expected: 0", check);
641 /* reconnect with options */
642 check = sdb_dbi_client_connect(client);
643 fail_unless(check == 0,
644 "repeated, with options sdb_dbi_client_connect() = %i; expected: 0",
645 check);
646 fail_unless(dbi_conn_connect_called == 3,
647 "repeated, with options sdb_dbi_client_create() called "
648 "dbi_conn_connect %i times; expected: 3", dbi_conn_connect_called);
650 /* The client takes ownership of the options, so no need to destroy it. */
651 }
652 END_TEST
654 START_TEST(test_sdb_dbi_client_check_conn)
655 {
656 int check = sdb_dbi_client_check_conn(client);
657 fail_unless(check == 0,
658 "sdb_dbi_client_check_conn() = %i; expected: 0", check);
660 /* the first call will actually connect to the database */
661 fail_unless(dbi_conn_connect_called == 1,
662 "sdb_dbi_client_check_conn() called dbi_conn_connect %i times; "
663 "expected: 1", dbi_conn_connect_called);
665 dbi_conn_connect_called = 0;
666 check = sdb_dbi_client_check_conn(client);
667 fail_unless(check == 0,
668 "sdb_dbi_client_check_conn() = %i; expected: 0", check);
670 /* should not reconnect */
671 fail_unless(dbi_conn_connect_called == 0,
672 "sdb_dbi_client_check_conn() called dbi_conn_connect %i time(s); "
673 "expected: 0", dbi_conn_connect_called);
674 }
675 END_TEST
677 START_TEST(test_sdb_dbi_exec_query)
678 {
679 size_t i;
681 int check = sdb_dbi_exec_query(client, "mockquery0", query_callback,
682 /* user_data = */ TEST_MAGIC, /* n = */ 0);
683 /* not connected yet */
684 fail_unless(check < 0,
685 "sdb_dbi_exec_query() = %i; expected: < 0", check);
687 connect();
689 for (i = 0; i < SDB_STATIC_ARRAY_LEN(mock_queries); ++i) {
690 mock_query_t *q = &mock_queries[i];
692 unsigned long long expected_callback_calls = 0;
694 dbi_conn_query_called = 0;
695 query_callback_called = 0;
696 dbi_result_free_called = 0;
698 /* sdb_dbi_exec_query will only use as many type arguments are needed,
699 * so we can safely pass in the maximum number of arguments required
700 * on each call */
701 check = sdb_dbi_exec_query(client, q->name, query_callback,
702 /* user_data = */ TEST_MAGIC, /* n = */ (int)q->nfields,
703 SDB_TYPE_INTEGER, SDB_TYPE_DECIMAL, SDB_TYPE_STRING,
704 SDB_TYPE_DATETIME, SDB_TYPE_BINARY);
705 fail_unless(check == 0,
706 "sdb_dbi_exec_query() = %i; expected: 0", check);
708 fail_unless(dbi_conn_query_called == 1,
709 "sdb_dbi_exec_query() called dbi_conn_query %i times; "
710 "expected: 1", dbi_conn_query_called);
712 if (q->nfields)
713 expected_callback_calls = q->nrows;
715 fail_unless(query_callback_called == expected_callback_calls,
716 "sdb_dbi_exec_query() did not call the registered callback "
717 "for each result row; got %i call%s; expected: 0",
718 query_callback_called,
719 (query_callback_called == 1) ? "" : "s");
721 fail_unless(dbi_result_free_called == 1,
722 "sdb_dbi_exec_query() did not free the query result object");
723 }
724 }
725 END_TEST
727 TEST_MAIN("utils::dbi")
728 {
729 TCase *tc = tcase_create("core");
730 tcase_add_checked_fixture(tc, setup, teardown);
731 tcase_add_test(tc, test_sdb_dbi_client_connect);
732 tcase_add_test(tc, test_sdb_dbi_client_check_conn);
733 tcase_add_test(tc, test_sdb_dbi_exec_query);
734 ADD_TCASE(tc);
735 }
736 TEST_MAIN_END
738 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */