1 /*
2  * Copyright (C) 2009 - 2012 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19 #include <stdio.h>
20 #include <string.h>
21 #include <libgda/libgda.h>
22 #include <sql-parser/gda-sql-parser.h>
23 #include <thread-wrapper/gda-thread-recordset.h>
24 #include "common.h"
25 
26 #define DBDIR "."
27 
28 static void test_cnc_obj (GdaConnection *tcnc);
29 static void test_cnc_status (GdaConnection *tcnc, gboolean opened);
30 
31 static gint test_select (GdaConnection *cnc, GdaConnection *tcnc);
32 static gint test_select_cursor (GdaConnection *cnc, GdaConnection *tcnc);
33 static gint test_insert (GdaConnection *tcnc);
34 static gint test_update (GdaConnection *tcnc);
35 static gint test_delete (GdaConnection *tcnc);
36 static gint test_meta_store (GdaConnection *cnc);
37 static gint test_meta_data (GdaConnection *cnc, GdaConnection *tcnc);
38 static gint test_signals (GdaConnection *cnc, GdaConnection *tcnc);
39 
40 int
main(int argc,char ** argv)41 main (int argc, char** argv)
42 {
43 	GdaConnection *cnc, *tcnc;
44         GError *error = NULL;
45 	gint failures = 0;
46 	gint ntests = 0;
47 	gchar *fname;
48 
49         gda_init ();
50 
51 	/* create test DB */
52 	fname = g_build_filename (ROOT_DIR, "tests", "multi-threading", "testdb.sql", NULL);
53         if (!create_sqlite_db (DBDIR, "testdb", fname, &error)) {
54                 g_print ("Cannot create test database: %s\n", error && error->message ?
55                          error->message : "no detail");
56                 return 1;
57         }
58         g_free (fname);
59 
60 
61         /* create a connection in threaded mode */
62         cnc = gda_connection_open_from_string ("SQlite", "DB_DIR=.;DB_NAME=testdb", NULL,
63 					       GDA_CONNECTION_OPTIONS_NONE, &error);
64         if (!cnc) {
65                 g_print ("ERROR opening connection: %s\n",
66                          error && error->message ? error->message : "No detail");
67                 return 1;
68         }
69         tcnc = gda_connection_open_from_string ("SQlite", "DB_DIR=.;DB_NAME=testdb", NULL,
70 					       GDA_CONNECTION_OPTIONS_THREAD_ISOLATED, &error);
71         if (!tcnc) {
72                 g_print ("ERROR opening connection in thread safe mode: %s\n",
73                          error && error->message ? error->message : "No detail");
74                 return 1;
75         }
76 
77 	test_cnc_obj (tcnc);
78 	test_cnc_status (tcnc, TRUE);
79         gda_connection_close (tcnc);
80 	test_cnc_status (tcnc, FALSE);
81         gda_connection_open (tcnc, NULL);
82 	test_cnc_status (tcnc, TRUE);
83 
84 	ntests ++;
85         failures += test_select (cnc, tcnc);
86 
87 	ntests ++;
88         failures += test_select_cursor (cnc, tcnc);
89 
90 	ntests ++;
91         failures += test_insert (tcnc);
92         failures += test_select (cnc, tcnc);
93         failures += test_select_cursor (cnc, tcnc);
94 
95 	ntests ++;
96         failures += test_update (tcnc);
97         failures += test_select (cnc, tcnc);
98         failures += test_select_cursor (cnc, tcnc);
99 
100 	ntests ++;
101         failures += test_delete (tcnc);
102         failures += test_select (cnc, tcnc);
103         failures += test_select_cursor (cnc, tcnc);
104 
105 	ntests ++;
106         failures += test_meta_data (cnc, tcnc);
107 
108 	ntests ++;
109         failures += test_meta_store (cnc);
110 
111 	ntests ++;
112 	failures += test_signals (cnc, tcnc);
113 
114         /* get rid of connection */
115         g_object_unref (cnc);
116         g_object_unref (tcnc);
117 	g_usleep (300000);
118 
119 	g_print ("TESTS COUNT: %d\n", ntests);
120 	g_print ("FAILURES: %d\n", failures);
121 
122 	return failures != 0 ? 1 : 0;
123 }
124 
125 static void
test_cnc_obj(GdaConnection * tcnc)126 test_cnc_obj (GdaConnection *tcnc)
127 {
128 	gboolean is_wrapper;
129 	g_object_get (G_OBJECT (tcnc), "is-wrapper", &is_wrapper, NULL);
130 	if (!is_wrapper) {
131 		g_print ("ERROR: connection is not a connection wrapper\n");
132                 exit (1);
133 	}
134 }
135 
136 static void
test_cnc_status(GdaConnection * tcnc,gboolean opened)137 test_cnc_status (GdaConnection *tcnc, gboolean opened)
138 {
139 	if (gda_connection_is_opened (tcnc) != opened) {
140 		g_print ("ERROR: connection is %s, expecting it %s\n",
141                          gda_connection_is_opened (tcnc) ? "Opened" : "Closed",
142                          opened ? "Opened" : "Closed");
143                 exit (1);
144 	}
145 }
146 
147 static gint
test_select(GdaConnection * cnc,GdaConnection * tcnc)148 test_select (GdaConnection *cnc, GdaConnection *tcnc)
149 {
150 	/* execute statement */
151 	gint failures = 0;
152 	GdaDataModel *model, *tmodel;
153 
154 	model = run_sql_select (cnc, "SELECT * FROM person");
155 	tmodel = run_sql_select (tcnc, "SELECT * FROM person");
156 	if (! data_models_equal (model, tmodel))
157 		failures ++;
158 
159 	if (strcmp (G_OBJECT_TYPE_NAME (tmodel), "GdaThreadRecordset")) {
160 		g_print ("ERROR: data model from SELECT's type is '%s' and not 'GdaThreadRecordset'\n",
161                          G_OBJECT_TYPE_NAME (tmodel));
162                 exit (1);
163 	}
164 	if (gda_data_select_get_connection (GDA_DATA_SELECT (tmodel)) != tcnc) {
165 		g_print ("ERROR: %s() returned wrong result\n", "gda_data_select_get_connection");
166 		failures ++;
167 	}
168 
169 	GdaDataModelIter *iter;
170 	GdaDataModelIter *titer;
171 	iter = gda_data_model_create_iter (model);
172 	titer = gda_data_model_create_iter (tmodel);
173 	if (!titer) {
174 		g_print ("ERROR: %s() returned NULL\n", "gda_data_model_create_iter");
175 		failures ++;
176 	}
177 	else {
178 		GdaDataModel *m1;
179 		g_object_get (G_OBJECT (titer), "data-model", &m1, NULL);
180 		if (m1 != tmodel) {
181 			g_print ("ERROR: \"data-model\" property of created iterator is wrong\n");
182 			failures ++;
183 		}
184 		g_object_unref (m1);
185 
186 		/* check iter's contents */
187 		guint ncols;
188 		ncols = g_slist_length (GDA_SET (titer)->holders);
189 		if (ncols != g_slist_length (GDA_SET (iter)->holders)) {
190 			g_print ("ERROR: threaded iterator is at the wrong number of columns: "
191 				 "%d when it should be at %d\n",
192 				 ncols, g_slist_length (GDA_SET (iter)->holders));
193 			failures ++;
194 			ncols = -1;
195 		}
196 		else
197 			g_assert (ncols > 0);
198 
199 		/* forward */
200 		if (gda_data_model_iter_is_valid (titer)) {
201 			g_print ("ERROR: threaded iterator is valid before any read\n");
202 			failures ++;
203 		}
204 		if (gda_data_model_iter_is_valid (iter)) {
205 			g_print ("ERROR: iterator is valid before any read\n");
206 			failures ++;
207 		}
208 		gint row;
209 		gboolean iterok;
210 		iterok = gda_data_model_iter_move_next (iter);
211 		iterok = gda_data_model_iter_move_next (titer) | iterok;
212 		for (row = 0; iterok; row++) {
213 			if (gda_data_model_iter_get_row (titer) != gda_data_model_iter_get_row (iter)) {
214 				g_print ("ERROR: threaded iterator is at the wrong row: %d when it should be at %d\n",
215 					 gda_data_model_iter_get_row (titer), gda_data_model_iter_get_row (iter));
216 				failures ++;
217 				break;
218 			}
219 			if (ncols > 0) {
220 				guint i;
221 				for (i = 0; i < ncols; i++) {
222 					const GValue *cv, *tcv;
223 					cv = gda_data_model_iter_get_value_at (iter, i);
224 					tcv = gda_data_model_iter_get_value_at (titer, i);
225 					if (gda_value_differ (cv, tcv)) {
226 						g_print ("ERROR: values in iterators differ at line %d, colunm %d\n",
227 							 row, i);
228 						failures ++;
229 					}
230 					//g_print ("%d,%d: %s\n", row, i, gda_value_stringify (tcv));
231 				}
232 			}
233 			gboolean v, tv;
234 			v = gda_data_model_iter_move_next (iter);
235 			tv = gda_data_model_iter_move_next (titer);
236 			if (!v || !tv)
237 				break;
238 		}
239 		if (gda_data_model_iter_is_valid (titer)) {
240 			g_print ("ERROR: threaded iterator is still valid\n");
241 			failures ++;
242 		}
243 		if (gda_data_model_iter_is_valid (iter)) {
244 			g_print ("ERROR: iterator is still valid\n");
245 			failures ++;
246 		}
247 
248 		/* backward */
249 		for (; row >= 0; row--) {
250 			iterok = gda_data_model_iter_move_next (iter);
251 			iterok = gda_data_model_iter_move_next (titer) | iterok;
252 			if (!iterok)
253 				break;
254 		}
255 		for (row = 0; iterok; row++) {
256 			if (gda_data_model_iter_get_row (titer) != gda_data_model_iter_get_row (iter)) {
257 				g_print ("ERROR: threaded iterator is at the wrong row: %d when it should be at %d\n",
258 					 gda_data_model_iter_get_row (titer), gda_data_model_iter_get_row (iter));
259 				failures ++;
260 				break;
261 			}
262 			if (ncols > 0) {
263 				guint i;
264 				for (i = 0; i < ncols; i++) {
265 					const GValue *cv, *tcv;
266 					cv = gda_data_model_iter_get_value_at (iter, i);
267 					tcv = gda_data_model_iter_get_value_at (titer, i);
268 					if (gda_value_differ (cv, tcv)) {
269 						g_print ("ERROR: values in iterators differ at line %d, colunm %d\n",
270 							 row, i);
271 						failures ++;
272 					}
273 					//g_print ("%d,%d: %s\n", row, i, gda_value_stringify (tcv));
274 				}
275 			}
276 			gboolean v, tv;
277 			v = gda_data_model_iter_move_prev (iter);
278 			tv = gda_data_model_iter_move_prev (titer);
279 			if (!v || !tv)
280 				break;
281 		}
282 		if (gda_data_model_iter_is_valid (titer)) {
283 			g_print ("ERROR: threaded iterator is still valid\n");
284 			failures ++;
285 		}
286 		if (gda_data_model_iter_is_valid (iter)) {
287 			g_print ("ERROR: iterator is still valid\n");
288 			failures ++;
289 		}
290 
291 		g_object_unref (iter);
292 		g_object_unref (titer);
293 	}
294 
295 	g_object_unref (model);
296 	g_object_unref (tmodel);
297 
298 	return failures;
299 }
300 
301 static gint
test_select_cursor(GdaConnection * cnc,GdaConnection * tcnc)302 test_select_cursor (GdaConnection *cnc, GdaConnection *tcnc)
303 {
304 	/* execute statement */
305 	gint failures = 0;
306 	GdaDataModel *model, *tmodel;
307 
308 	model = run_sql_select_cursor (cnc, "SELECT * FROM person");
309 	tmodel = run_sql_select_cursor (tcnc, "SELECT * FROM person");
310 
311 	if (gda_data_model_get_access_flags (model) !=
312 	    gda_data_model_get_access_flags (tmodel)) {
313 		g_print ("ERROR: data models' access flags differ: %d and %d for threaded model\n",
314                          gda_data_model_get_access_flags (model),
315 			 gda_data_model_get_access_flags (tmodel));
316 		failures ++;
317 		return failures;
318 	}
319 
320 	if (strcmp (G_OBJECT_TYPE_NAME (tmodel), "GdaThreadRecordset")) {
321 		g_print ("ERROR: data model from SELECT's type is '%s' and not 'GdaThreadRecordset'\n",
322                          G_OBJECT_TYPE_NAME (tmodel));
323                 exit (1);
324 	}
325 	if (gda_data_select_get_connection (GDA_DATA_SELECT (tmodel)) != tcnc) {
326 		g_print ("ERROR: %s() returned wrong result\n", "gda_data_select_get_connection");
327 		failures ++;
328 	}
329 
330 	GdaDataModelIter *iter, *titer;
331 	iter = gda_data_model_create_iter (model);
332 	titer = gda_data_model_create_iter (tmodel);
333 	if (!titer) {
334 		g_print ("ERROR: %s() returned NULL\n", "gda_data_model_create_iter");
335 		failures ++;
336 	}
337 	else {
338 		/* iter's properties */
339 		GdaDataModel *m1;
340 		g_object_get (G_OBJECT (titer), "data-model", &m1, NULL);
341 		if (m1 != tmodel) {
342 			g_print ("ERROR: \"data-model\" property of created iterator is wrong\n");
343 			failures ++;
344 		}
345 		g_object_unref (m1);
346 
347 		/* check iter's contents */
348 		guint ncols;
349 		ncols = g_slist_length (GDA_SET (titer)->holders);
350 		if (ncols != g_slist_length (GDA_SET (iter)->holders)) {
351 			g_print ("ERROR: threaded iterator is at the wrong number of columns: "
352 				 "%d when it should be at %d\n",
353 				 ncols, g_slist_length (GDA_SET (iter)->holders));
354 			failures ++;
355 			ncols = -1;
356 		}
357 		else
358 			g_assert (ncols > 0);
359 
360 
361 		if (gda_data_model_iter_is_valid (titer)) {
362 			g_print ("ERROR: threaded iterator is valid before any read\n");
363 			failures ++;
364 		}
365 		if (gda_data_model_iter_is_valid (iter)) {
366 			g_print ("ERROR: iterator is valid before any read\n");
367 			failures ++;
368 		}
369 		gint row;
370 		gda_data_model_iter_move_next (iter);
371 		gda_data_model_iter_move_next (titer);
372 		for (row = 0; ; row++) {
373 			if (gda_data_model_iter_get_row (titer) != gda_data_model_iter_get_row (iter)) {
374 				g_print ("ERROR: threaded iterator is at the wrong row: %d when it should be at %d\n",
375 					 gda_data_model_iter_get_row (titer), gda_data_model_iter_get_row (iter));
376 				failures ++;
377 				break;
378 			}
379 			if (ncols > 0) {
380 				guint i;
381 				for (i = 0; i < ncols; i++) {
382 					const GValue *cv, *tcv;
383 					cv = gda_data_model_iter_get_value_at (iter, i);
384 					tcv = gda_data_model_iter_get_value_at (titer, i);
385 					if (gda_value_differ (cv, tcv)) {
386 						g_print ("ERROR: values in iterators differ at line %d, colunm %d\n",
387 							 row, i);
388 						failures ++;
389 					}
390 					//g_print ("%d,%d: %s\n", row, i, gda_value_stringify (tcv));
391 				}
392 			}
393 			gboolean v, tv;
394 			v = gda_data_model_iter_move_next (iter);
395 			tv = gda_data_model_iter_move_next (titer);
396 			if (!v || !tv)
397 				break;
398 		}
399 		if (gda_data_model_iter_is_valid (titer)) {
400 			g_print ("ERROR: threaded iterator is still valid\n");
401 			failures ++;
402 		}
403 		if (gda_data_model_iter_is_valid (iter)) {
404 			g_print ("ERROR: iterator is still valid\n");
405 			failures ++;
406 		}
407 
408 		g_object_unref (iter);
409 		g_object_unref (titer);
410 	}
411 
412 	g_object_unref (model);
413 	g_object_unref (tmodel);
414 
415 	return failures;
416 }
417 
418 static gint
test_insert(GdaConnection * tcnc)419 test_insert (GdaConnection *tcnc)
420 {
421 	gint failures = 0;
422 	if (!run_sql_non_select (tcnc, "INSERT INTO person (name, age) VALUES ('Alice', 12)")) {
423 		g_print ("ERROR: can't INSERT into threaded connection\n");
424 			failures ++;
425 	}
426 	return failures;
427 }
428 
429 static gint
test_update(GdaConnection * tcnc)430 test_update (GdaConnection *tcnc)
431 {
432 	gint failures = 0;
433 	if (!run_sql_non_select (tcnc, "UPDATE person set name='Alf' where name='Grat'")) {
434 		g_print ("ERROR: can't UPDATE threaded connection\n");
435 			failures ++;
436 	}
437 	return failures;
438 }
439 
440 static gint
test_delete(GdaConnection * tcnc)441 test_delete (GdaConnection *tcnc)
442 {
443 	gint failures = 0;
444 	if (!run_sql_non_select (tcnc, "DELETE FROM person WHERE name='Alf'")) {
445 		g_print ("ERROR: can't DELETE from threaded connection\n");
446 			failures ++;
447 	}
448 	return failures;
449 }
450 
451 static gint
test_meta_store(GdaConnection * cnc)452 test_meta_store (GdaConnection *cnc)
453 {
454 	GdaMetaStore *store;
455 	GdaConnection *tcnc;
456 	GError *error = NULL;
457 
458 	g_print ("=== Starting test where threaded connection is used internally by a meta store\n");
459 	tcnc = gda_connection_open_from_string ("SQlite", "DB_DIR=.;DB_NAME=storedb", NULL,
460 						GDA_CONNECTION_OPTIONS_THREAD_ISOLATED, &error);
461         if (!tcnc) {
462                 g_print ("ERROR opening connection in thread safe mode: %s\n",
463                          error && error->message ? error->message : "No detail");
464                 return 1;
465         }
466 
467 	store = GDA_META_STORE (g_object_new (GDA_TYPE_META_STORE, "cnc", tcnc, NULL));
468 	g_object_unref (tcnc);
469 	g_object_set (G_OBJECT (cnc), "meta-store", store, NULL);
470 	g_object_unref (store);
471 
472 	if (!gda_connection_update_meta_store (cnc, NULL, &error)) {
473 		g_print ("ERROR in gda_connection_update_meta_store(): %s\n",
474                          error && error->message ? error->message : "No detail");
475                 return 1;
476 	}
477 
478 	GdaDataModel *model;
479 	model = gda_meta_store_extract (store, "SELECT * FROM _tables", NULL);
480 	if (gda_data_model_get_n_rows (model) != 1) {
481 		g_print ("ERROR in gda_connection_update_meta_store(): the _tables table "
482 			 "should have exactly 1 row\n");
483 		g_object_unref (model);
484 		return 1;
485 	}
486 	g_object_unref (model);
487 
488 	return 0;
489 }
490 
491 static gint
compare_meta_data(GdaMetaStore * store1,GdaMetaStruct * mstruct1,GdaMetaStore * store2,GdaMetaStruct * mstruct2)492 compare_meta_data (GdaMetaStore *store1, GdaMetaStruct *mstruct1,
493 		   GdaMetaStore *store2, GdaMetaStruct *mstruct2)
494 {
495 	GSList *all_dbo_list1, *all_dbo_list2, *list;
496 
497 	all_dbo_list1 = gda_meta_struct_get_all_db_objects (mstruct1);
498 	all_dbo_list2 = gda_meta_struct_get_all_db_objects (mstruct2);
499 
500 	for (list = all_dbo_list1; list; list = list->next) {
501 		GdaMetaDbObject *dbo1, *dbo2;
502 		GValue *v1, *v2, *v3;
503 		dbo1 = GDA_META_DB_OBJECT (list->data);
504 
505 		g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), dbo1->obj_catalog);
506 		g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), dbo1->obj_schema);
507 		g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), dbo1->obj_name);
508 		dbo2 = gda_meta_struct_get_db_object (mstruct2, v1, v2, v3);
509 		gda_value_free (v1);
510 		gda_value_free (v2);
511 		gda_value_free (v3);
512 		if (!dbo2) {
513 			g_print ("Error: Could not find object %s.%s.%s\n",
514 				 dbo1->obj_catalog,
515 				 dbo1->obj_schema,
516 				 dbo1->obj_name);
517 			return 1;
518 		}
519 
520 		if (dbo1->obj_type != GDA_META_DB_TABLE)
521 			continue;
522 
523 		g_print ("Checking meta store's table: %s\n", dbo1->obj_name);
524 
525 		GdaDataModel *m1, *m2;
526 		gchar *str;
527 		str = g_strdup_printf ("SELECT * FROM %s", dbo1->obj_name);
528 		m1 = gda_meta_store_extract (store1, str, NULL);
529 		m2 = gda_meta_store_extract (store2, str, NULL);
530 		g_free (str);
531 
532 		GdaDataComparator *cmp;
533 		GError *error = NULL;
534 		cmp = (GdaDataComparator*) gda_data_comparator_new (m1, m2);
535 		g_object_unref (m1);
536 		g_object_unref (m2);
537 		if (! gda_data_comparator_compute_diff (cmp, &error)) {
538 			g_print ("Can't compute data model differences for %s.%s.%s: %s\n",
539 				 dbo1->obj_catalog,
540 				 dbo1->obj_schema,
541 				 dbo1->obj_name,
542 				 error && error->message ? error->message : "No detail");
543 			g_object_unref (cmp);
544 			return 1;
545 		}
546 		if (gda_data_comparator_get_n_diffs (cmp) != 0) {
547 			g_print ("There are some data model differences!\n");
548 			g_object_unref (cmp);
549 			return 1;
550 		}
551 		g_object_unref (cmp);
552 	}
553 
554 	g_slist_free (all_dbo_list1);
555 	g_slist_free (all_dbo_list2);
556 
557 	return 0;
558 }
559 
560 static gint
test_meta_data(GdaConnection * cnc,GdaConnection * tcnc)561 test_meta_data (GdaConnection *cnc, GdaConnection *tcnc)
562 {
563 	GError *error = NULL;
564 
565 	g_print ("=== Starting test where updating the threaded connection's meta data\n");
566 	if (!gda_connection_update_meta_store (tcnc, NULL, &error)) {
567 		g_print ("ERROR in gda_connection_update_meta_store() applied to threaded connection: %s\n",
568                          error && error->message ? error->message : "No detail");
569                 return 1;
570 	}
571 	if (!gda_connection_update_meta_store (cnc, NULL, &error)) {
572 		g_print ("ERROR in gda_connection_update_meta_store() applied to non threaded connection: %s\n",
573                          error && error->message ? error->message : "No detail");
574                 return 1;
575 	}
576 
577 	GdaMetaStruct *mstruct, *tmstruct;
578 	mstruct = gda_meta_store_schema_get_structure (gda_connection_get_meta_store (cnc), NULL);
579 	tmstruct = gda_meta_store_schema_get_structure (gda_connection_get_meta_store (tcnc), NULL);
580 	g_assert (mstruct);
581 	g_assert (tmstruct);
582 
583 	return compare_meta_data (gda_connection_get_meta_store (cnc), mstruct,
584 				  gda_connection_get_meta_store (tcnc), tmstruct);
585 }
586 
587 
588 static void test_sig_error_cb (GdaConnection *cnc, GdaConnectionEvent *error, GdaConnectionEvent **ev);
589 static void test_sig_bool_cb (GdaConnection *cnc, gboolean *out_called);
590 static gint
test_signals(GdaConnection * cnc,GdaConnection * tcnc)591 test_signals (GdaConnection *cnc, GdaConnection *tcnc)
592 {
593 	gint failures = 0;
594 
595 	/* test the "error" signal */
596 	GdaConnectionEvent *ev = NULL, *tev = NULL;
597 	g_signal_connect (G_OBJECT (cnc), "error",
598 			  G_CALLBACK (test_sig_error_cb), &ev);
599 	g_signal_connect (G_OBJECT (tcnc), "error",
600 			  G_CALLBACK (test_sig_error_cb), &tev);
601 	run_sql_non_select (cnc, "DELETE FROM person WHERE name = ##name::string");
602 	run_sql_non_select (tcnc, "DELETE FROM person WHERE name = ##name::string");
603 
604 	g_assert (ev);
605 	if (!tev) {
606 		g_print ("ERROR: the threaded connection does not emit the \"error\" signal\n");
607 		return 1;
608 	}
609 
610 	/* test the "conn_to_close" and "conn-closed" signal */
611 	gboolean called = FALSE, called2 = FALSE;
612 	g_signal_connect (G_OBJECT (tcnc), "conn-to-close",
613 			  G_CALLBACK (test_sig_bool_cb), &called);
614 	g_signal_connect (G_OBJECT (tcnc), "conn-closed",
615 			  G_CALLBACK (test_sig_bool_cb), &called2);
616 	gda_connection_close (tcnc);
617 	if (!called) {
618 		g_print ("ERROR: the threaded connection does not emit the \"conn-to-close\" signal\n");
619 		return 1;
620 	}
621 	if (!called2) {
622 		g_print ("ERROR: the threaded connection does not emit the \"conn-closed\" signal\n");
623 		return 1;
624 	}
625 	g_signal_handlers_disconnect_by_func (G_OBJECT (tcnc),
626 					      G_CALLBACK (test_sig_bool_cb), &called);
627 	g_signal_handlers_disconnect_by_func (G_OBJECT (tcnc),
628 					      G_CALLBACK (test_sig_bool_cb), &called2);
629 
630 	/* test the "conn-opened" signal */
631 	called = FALSE;
632 	g_signal_connect (G_OBJECT (tcnc), "conn-opened",
633 			  G_CALLBACK (test_sig_bool_cb), &called);
634 	gda_connection_open (tcnc, NULL);
635 	g_assert (gda_connection_is_opened (tcnc));
636 	if (!called) {
637 		g_print ("ERROR: the threaded connection does not emit the \"conn-opened\" signal\n");
638 		return 1;
639 	}
640 	g_signal_handlers_disconnect_by_func (G_OBJECT (tcnc),
641 					      G_CALLBACK (test_sig_bool_cb), &called);
642 
643 	/* test the "transaction-status-changed" signal */
644 	called = FALSE;
645 	g_signal_connect (G_OBJECT (tcnc), "transaction-status-changed",
646 			  G_CALLBACK (test_sig_bool_cb), &called);
647 	g_assert (gda_connection_begin_transaction (tcnc, NULL, GDA_TRANSACTION_ISOLATION_UNKNOWN, NULL));
648 	if (!called) {
649 		g_print ("ERROR: the threaded connection does not emit the \"transaction-status-changed\" signal\n");
650 		return 1;
651 	}
652 	g_signal_handlers_disconnect_by_func (G_OBJECT (tcnc),
653 					      G_CALLBACK (test_sig_bool_cb), &called);
654 
655 	/* test the "dsn_changed" signal */
656 	called = FALSE;
657 	gda_connection_close (tcnc);
658 	if (gda_config_get_dsn_info ("SalesTest")) {
659 		g_signal_connect (G_OBJECT (tcnc), "dsn-changed",
660 				  G_CALLBACK (test_sig_bool_cb), &called);
661 		g_object_set (G_OBJECT (tcnc), "dsn", "SalesTest", NULL);
662 		if (!called) {
663 			g_print ("ERROR: the threaded connection does not emit the \"dsn-changed\" signal\n");
664 			return 1;
665 		}
666 		g_signal_handlers_disconnect_by_func (G_OBJECT (tcnc),
667 						      G_CALLBACK (test_sig_bool_cb), &called);
668 	}
669 
670 	return failures;
671 }
672 
673 static void
test_sig_error_cb(GdaConnection * cnc,GdaConnectionEvent * error,GdaConnectionEvent ** ev)674 test_sig_error_cb (GdaConnection *cnc, GdaConnectionEvent *error, GdaConnectionEvent **ev)
675 {
676 	*ev = error;
677 }
678 
679 static void
test_sig_bool_cb(GdaConnection * cnc,gboolean * out_called)680 test_sig_bool_cb (GdaConnection *cnc, gboolean *out_called)
681 {
682 	*out_called = TRUE;
683 }
684