Code

parser: Let the TIMESERIES command accept optional data-source names.
[sysdb.git] / t / unit / utils / dbi_test.c
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)
167         if (pInst)
168                 *pInst = INST;
169         return 0;
170 } /* dbi_initialize_r */
172 void
173 dbi_shutdown_r(dbi_inst inst)
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)
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)
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)
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)
218         dbi_shutdown_r(INST);
219 } /* dbi_shutdown */
221 dbi_driver
222 dbi_driver_open(const char *name)
224         return dbi_driver_open_r(name, INST);
225 } /* dbi_driver_open */
227 dbi_driver
228 dbi_driver_list(dbi_driver curr)
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)
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)
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)
252         return NULL;
253 } /* dbi_conn_get_option_list */
255 dbi_conn
256 dbi_conn_open(dbi_driver driver)
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)
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)
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)
284         return;
285 } /* dbi_conn_close */
287 int
288 dbi_conn_error(dbi_conn conn, const char **errmsg)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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. */
652 END_TEST
654 START_TEST(test_sdb_dbi_client_check_conn)
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);
675 END_TEST
677 START_TEST(test_sdb_dbi_exec_query)
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         }
725 END_TEST
727 TEST_MAIN("utils::dbi")
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);
736 TEST_MAIN_END
738 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */