1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2013, Openismus GmbH
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Tristan Van Berkom <tristanvb@openismus.com>
18  */
19 
20 #include "evolution-data-server-config.h"
21 
22 #ifdef HAVE_SYS_WAIT_H
23 #include <sys/wait.h>
24 #endif
25 
26 #include <locale.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 
31 #include "data-test-utils.h"
32 
33 static const gchar *args_data_dir = NULL;
34 
35 void
data_test_utils_read_args(gint argc,gchar ** argv)36 data_test_utils_read_args (gint argc,
37 			   gchar **argv)
38 {
39 	gint ii;
40 
41 	for (ii = 0; ii < argc; ii++) {
42 		if (g_strcmp0 (argv[ii], "--data-dir") == 0) {
43 			if (ii + 1 < argc)
44 				args_data_dir = argv[ii + 1];
45 			break;
46 		}
47 	}
48 }
49 
50 gchar *
new_vcard_from_test_case(const gchar * case_name)51 new_vcard_from_test_case (const gchar *case_name)
52 {
53 	gchar *filename;
54 	gchar *case_filename;
55 	GFile * file;
56 	GError *error = NULL;
57 	gchar *vcard;
58 
59 	case_filename = g_strdup_printf ("%s.vcf", case_name);
60 
61 	/* In the case of installed tests, they run in ${pkglibexecdir}/installed-tests
62 	 * and the vcards are installed in ${pkglibexecdir}/installed-tests/vcards
63 	 */
64 	if (g_getenv ("TEST_INSTALLED_SERVICES") != NULL) {
65 		filename = g_build_filename (INSTALLED_TEST_DIR, "vcards", case_filename, NULL);
66 	} else {
67 		if (!args_data_dir) {
68 			g_warning ("Data directory not set, pass it with `--data-dir PATH`");
69 			exit(1);
70 		}
71 
72 		filename = g_build_filename (args_data_dir, case_filename, NULL);
73 	}
74 
75 	file = g_file_new_for_path (filename);
76 	if (!g_file_load_contents (file, NULL, &vcard, NULL, NULL, &error))
77 		g_error (
78 			"failed to read test contact file '%s': %s",
79 			filename, error->message);
80 
81 	g_free (case_filename);
82 	g_free (filename);
83 	g_object_unref (file);
84 
85 	return vcard;
86 }
87 
88 EContact *
new_contact_from_test_case(const gchar * case_name)89 new_contact_from_test_case (const gchar *case_name)
90 {
91 	gchar *vcard;
92 	EContact *contact = NULL;
93 
94 	vcard = new_vcard_from_test_case (case_name);
95 	if (vcard)
96 		contact = e_contact_new_from_vcard (vcard);
97 	g_free (vcard);
98 
99 	if (!contact)
100 		g_error (
101 			"failed to construct contact from test case '%s'",
102 			case_name);
103 
104 	return contact;
105 }
106 
107 void
add_contact_from_test_case(EbSqlFixture * fixture,const gchar * case_name,EContact ** ret_contact)108 add_contact_from_test_case (EbSqlFixture *fixture,
109                             const gchar *case_name,
110                             EContact **ret_contact)
111 {
112 	EContact *contact;
113 	GError *error = NULL;
114 
115 	contact = new_contact_from_test_case (case_name);
116 
117 	if (!e_book_sqlite_add_contact (fixture->ebsql,
118 					contact, case_name,
119 					FALSE, NULL, &error))
120 		g_error ("Failed to add contact: %s", error->message);
121 
122 	if (ret_contact)
123 		*ret_contact = g_object_ref (contact);
124 
125 	/* Hold on to this so we can load vcards */
126 	g_hash_table_insert (fixture->contacts, g_strdup (case_name), contact);
127 }
128 
129 static void
delete_work_directory(const gchar * filename)130 delete_work_directory (const gchar *filename)
131 {
132 	/* XXX Instead of complex error checking here, we should ideally use
133 	 * a recursive GDir / g_unlink() function.
134 	 *
135 	 * We cannot use GFile and the recursive delete function without
136 	 * corrupting our contained D-Bus environment with service files
137 	 * from the OS.
138 	 */
139 	const gchar *argv[] = { "/bin/rm", "-rf", filename, NULL };
140 	gboolean spawn_succeeded;
141 	gint exit_status;
142 
143 	spawn_succeeded = g_spawn_sync (
144 		NULL, (gchar **) argv, NULL, 0, NULL, NULL,
145 					NULL, NULL, &exit_status, NULL);
146 
147 	g_assert (spawn_succeeded);
148 	#ifndef G_OS_WIN32
149 	g_assert (WIFEXITED (exit_status));
150 	g_assert_cmpint (WEXITSTATUS (exit_status), ==, 0);
151 	#else
152 	g_assert_cmpint (exit_status, ==, 0);
153 	#endif
154 }
155 
156 ESourceBackendSummarySetup *
setup_empty_book(void)157 setup_empty_book (void)
158 {
159 	ESourceBackendSummarySetup *setup;
160 	ESource *scratch;
161 	GError *error = NULL;
162 
163 	scratch = e_source_new_with_uid ("test-source", NULL, &error);
164 	if (!scratch)
165 		g_error ("Error creating scratch source");
166 
167 	/* This is a bit of a cheat */
168 	setup = g_object_new (E_TYPE_SOURCE_BACKEND_SUMMARY_SETUP, "source", scratch, NULL);
169 	e_source_backend_summary_setup_set_summary_fields (
170 		setup,
171 							   /* We don't use this field in our tests anyway */
172 		E_CONTACT_FILE_AS,
173 		0);
174 
175 	g_object_unref (scratch);
176 
177 	return setup;
178 }
179 
180 static gchar *
fetch_vcard_from_hash(const gchar * uid,const gchar * extra,gpointer user_data)181 fetch_vcard_from_hash (const gchar *uid,
182                        const gchar *extra,
183                        gpointer user_data)
184 {
185 	EbSqlFixture *fixture = user_data;
186 	EContact     *contact;
187 
188 	g_assert (extra && extra[0]);
189 
190 	/* vCards not stored in shallow addressbooks, instead loaded on the fly */
191 	contact = g_hash_table_lookup (fixture->contacts, extra);
192 	if (contact)
193 		return e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
194 
195 	return NULL;
196 }
197 
198 static void
contact_change_cb(EbSqlChangeType change_type,const gchar * uid,const gchar * extra,const gchar * vcard,gpointer user_data)199 contact_change_cb (EbSqlChangeType change_type,
200                    const gchar *uid,
201                    const gchar *extra,
202                    const gchar *vcard,
203                    gpointer user_data)
204 {
205 	EbSqlFixture *fixture = user_data;
206 	EContact *contact;
207 
208 	if (change_type == EBSQL_CHANGE_LOCALE_CHANGED)
209 		fixture->n_locale_changes++;
210 	else if (change_type == EBSQL_CHANGE_CONTACT_ADDED)
211 		fixture->n_add_changes++;
212 
213 	/* Replace the current contact with the new one */
214 	contact = e_contact_new_from_vcard (vcard);
215 	g_hash_table_insert (fixture->contacts, g_strdup (extra), contact);
216 }
217 
218 void
e_sqlite_fixture_setup(EbSqlFixture * fixture,gconstpointer user_data)219 e_sqlite_fixture_setup (EbSqlFixture *fixture,
220                         gconstpointer user_data)
221 {
222 	EbSqlClosure *closure = (EbSqlClosure *) user_data;
223 	ESourceBackendSummarySetup *setup = NULL;
224 	gchar  *filename, *directory;
225 	GError *error = NULL;
226 
227 	fixture->contacts =
228 		g_hash_table_new_full (
229 			g_str_hash,
230 			g_str_equal,
231 			g_free,
232 			g_object_unref);
233 
234 	/* Cleanup from last test */
235 	directory = g_build_filename (g_get_tmp_dir (), "test-sqlite-cache", NULL);
236 	delete_work_directory (directory);
237 	g_free (directory);
238 	filename = g_build_filename (g_get_tmp_dir (), "test-sqlite-cache", "contacts.db", NULL);
239 
240 	if (closure->setup_summary)
241 		setup = closure->setup_summary ();
242 
243 	if (closure->without_vcards)
244 		fixture->ebsql = e_book_sqlite_new_full (
245 			filename,
246 			NULL,
247 			setup,
248 			fetch_vcard_from_hash,
249 			contact_change_cb,
250 			fixture,
251 			NULL, NULL,
252 			&error);
253 	else
254 		fixture->ebsql = e_book_sqlite_new_full (
255 			filename,
256 			NULL,
257 			setup,
258 			NULL,
259 			contact_change_cb,
260 			fixture,
261 			NULL, NULL,
262 			&error);
263 
264 	g_clear_object (&setup);
265 
266 	if (!fixture->ebsql)
267 		g_error ("Failed to create the SQLite: %s", error->message);
268 
269 	g_free (filename);
270 }
271 
272 void
e_sqlite_fixture_teardown(EbSqlFixture * fixture,gconstpointer user_data)273 e_sqlite_fixture_teardown (EbSqlFixture *fixture,
274                            gconstpointer user_data)
275 {
276 	g_object_unref (fixture->ebsql);
277 	g_hash_table_destroy (fixture->contacts);
278 }
279 
280 void
e_sqlite_cursor_fixture_setup(EbSqlCursorFixture * fixture,gconstpointer user_data)281 e_sqlite_cursor_fixture_setup (EbSqlCursorFixture *fixture,
282                                gconstpointer user_data)
283 {
284 	EbSqlFixture       *base_fixture = (EbSqlFixture   *) fixture;
285 	EbSqlCursorClosure *data = (EbSqlCursorClosure *) user_data;
286 	EContactField       sort_fields[] = { E_CONTACT_FAMILY_NAME, E_CONTACT_GIVEN_NAME };
287 	EBookCursorSortType sort_types[] = { data->sort_type, data->sort_type };
288 	GSList *contacts = NULL;
289 	GSList *extra_list = NULL;
290 	GError *error = NULL;
291 	gint i;
292 	gchar *sexp = NULL;
293 
294 	e_sqlite_fixture_setup (base_fixture, user_data);
295 
296 	if (data->locale)
297 		e_sqlite_cursor_fixture_set_locale (fixture, data->locale);
298 	else
299 		e_sqlite_cursor_fixture_set_locale (fixture, "en_US.UTF-8");
300 
301 	for (i = 0; i < N_SORTED_CONTACTS; i++) {
302 		gchar *case_name = g_strdup_printf ("sorted-%d", i + 1);
303 		gchar *vcard;
304 		EContact *contact;
305 
306 		vcard = new_vcard_from_test_case (case_name);
307 		contact = e_contact_new_from_vcard (vcard);
308 		contacts = g_slist_prepend (contacts, contact);
309 		extra_list = g_slist_prepend (extra_list, case_name);
310 
311 		g_free (vcard);
312 
313 		fixture->contacts[i] = contact;
314 
315 		/* These need to be added to the hash as well */
316 		g_hash_table_insert (
317 			base_fixture->contacts,
318 			g_strdup (case_name),
319 			g_object_ref (contact));
320 	}
321 
322 	if (!e_book_sqlite_add_contacts (base_fixture->ebsql,
323 					 contacts, extra_list,
324 					 FALSE, NULL, &error)) {
325 		/* Dont complain here, we re-use the same addressbook for multiple tests
326 		 * and we can't add the same contacts twice
327 		 */
328 		if (g_error_matches (error, E_BOOK_SQLITE_ERROR,
329 				     E_BOOK_SQLITE_ERROR_CONSTRAINT))
330 			g_clear_error (&error);
331 		else
332 			g_error ("Failed to add test contacts: %s", error->message);
333 	}
334 
335 	g_slist_free (contacts);
336 	g_slist_free_full (extra_list, g_free);
337 
338 	/* Allow a surrounding fixture setup to add a query here */
339 	if (fixture->query) {
340 		sexp = e_book_query_to_string (fixture->query);
341 		e_book_query_unref (fixture->query);
342 		fixture->query = NULL;
343 	}
344 
345 	fixture->cursor = e_book_sqlite_cursor_new (
346 		base_fixture->ebsql, sexp,
347 		sort_fields, sort_types, 2, &error);
348 
349 	if (!fixture->cursor)
350 		g_error ("Failed to create cursor: %s\n", error->message);
351 
352 	g_free (sexp);
353 }
354 
355 void
e_sqlite_cursor_fixture_filtered_setup(EbSqlCursorFixture * fixture,gconstpointer user_data)356 e_sqlite_cursor_fixture_filtered_setup (EbSqlCursorFixture *fixture,
357                                         gconstpointer user_data)
358 {
359 	fixture->query = e_book_query_field_test (E_CONTACT_EMAIL, E_BOOK_QUERY_ENDS_WITH, ".com");
360 
361 	e_sqlite_cursor_fixture_setup (fixture, user_data);
362 }
363 
364 void
e_sqlite_cursor_fixture_teardown(EbSqlCursorFixture * fixture,gconstpointer user_data)365 e_sqlite_cursor_fixture_teardown (EbSqlCursorFixture *fixture,
366                                     gconstpointer user_data)
367 {
368 	EbSqlFixture *base_fixture = (EbSqlFixture   *) fixture;
369 	gint i;
370 
371 	for (i = 0; i < N_SORTED_CONTACTS; i++) {
372 		if (fixture->contacts[i])
373 			g_object_unref (fixture->contacts[i]);
374 	}
375 
376 	e_book_sqlite_cursor_free (base_fixture->ebsql, fixture->cursor);
377 	e_sqlite_fixture_teardown (base_fixture, user_data);
378 }
379 
380 void
e_sqlite_cursor_fixture_set_locale(EbSqlCursorFixture * fixture,const gchar * locale)381 e_sqlite_cursor_fixture_set_locale (EbSqlCursorFixture *fixture,
382                                     const gchar *locale)
383 {
384 	EbSqlFixture *base_fixture = (EbSqlFixture   *) fixture;
385 	GError *error = NULL;
386 
387 	if (!e_book_sqlite_set_locale (base_fixture->ebsql,
388 				       locale, NULL, &error))
389 		g_error ("Failed to set locale: %s", error->message);
390 }
391 
392 static gint
find_contact_data(EbSqlSearchData * data,const gchar * uid)393 find_contact_data (EbSqlSearchData *data,
394                    const gchar *uid)
395 {
396 	return g_strcmp0 (data->uid, uid);
397 }
398 
399 void
assert_contacts_order_slist(GSList * results,GSList * uids)400 assert_contacts_order_slist (GSList *results,
401                              GSList *uids)
402 {
403 	gint position = -1;
404 	GSList *link, *l;
405 
406 	/* Assert that all passed UIDs are found in the
407 	 * results, and that those UIDs are in the
408 	 * specified order.
409 	 */
410 	for (l = uids; l; l = l->next) {
411 		const gchar *uid = l->data;
412 		gint new_position;
413 
414 		link = g_slist_find_custom (results, uid, (GCompareFunc) find_contact_data);
415 		if (!link)
416 			g_error ("Specified uid '%s' was not found in results", uid);
417 
418 		new_position = g_slist_position (results, link);
419 		g_assert_cmpint (new_position, >, position);
420 		position = new_position;
421 	}
422 
423 }
424 
425 void
assert_contacts_order(GSList * results,const gchar * first_uid,...)426 assert_contacts_order (GSList *results,
427                        const gchar *first_uid,
428                        ...)
429 {
430 	GSList *uids = NULL;
431 	gchar *uid;
432 	va_list args;
433 
434 	g_assert (first_uid);
435 
436 	uids = g_slist_append (uids, (gpointer) first_uid);
437 
438 	va_start (args, first_uid);
439 	uid = va_arg (args, gchar *);
440 	while (uid) {
441 		uids = g_slist_append (uids, uid);
442 		uid = va_arg (args, gchar *);
443 	}
444 	va_end (args);
445 
446 	assert_contacts_order_slist (results, uids);
447 	g_slist_free (uids);
448 }
449 
450 void
print_results(GSList * results)451 print_results (GSList *results)
452 {
453 	GSList *l;
454 
455 	if (g_getenv ("TEST_DEBUG") == NULL)
456 		return;
457 
458 	g_print ("\nPRINTING RESULTS:\n");
459 
460 	for (l = results; l; l = l->next) {
461 		EbSqlSearchData *data = l->data;
462 
463 		g_print ("\n%s\n", data->vcard);
464 	}
465 
466 	g_print ("\nRESULT LIST_FINISHED\n");
467 }
468 
469 /********************************************
470  *           Move By Test Helpers
471  ********************************************/
472 #define DEBUG_FIXTURE        0
473 
474 static StepData *
step_test_new_internal(const gchar * test_path,const gchar * locale,gboolean store_vcards,gboolean empty_book)475 step_test_new_internal (const gchar *test_path,
476                         const gchar *locale,
477                         gboolean store_vcards,
478                         gboolean empty_book)
479 {
480 	StepData *data;
481 
482 	data = g_slice_new0 (StepData);
483 
484 	data->parent.locale = g_strdup (locale);
485 	data->parent.sort_type = E_BOOK_CURSOR_SORT_ASCENDING;
486 
487 	data->parent.parent.without_vcards = (store_vcards == FALSE);
488 	if (empty_book)
489 		data->parent.parent.setup_summary = setup_empty_book;
490 
491 	data->path = g_strdup (test_path);
492 
493 	return data;
494 }
495 
496 static void
step_test_free(StepData * data)497 step_test_free (StepData *data)
498 {
499 	GList *l;
500 
501 	g_free (data->path);
502 	g_free ((gchar *) data->parent.locale);
503 
504 	for (l = data->assertions; l; l = l->next) {
505 		StepAssertion *assertion = l->data;
506 
507 		g_free (assertion->locale);
508 		g_slice_free (StepAssertion, assertion);
509 	}
510 
511 	g_slice_free (StepData, data);
512 }
513 
514 StepData *
step_test_new(const gchar * test_prefix,const gchar * test_path,const gchar * locale,gboolean store_vcards,gboolean empty_book)515 step_test_new (const gchar *test_prefix,
516                const gchar *test_path,
517                const gchar *locale,
518                gboolean store_vcards,
519                gboolean empty_book)
520 {
521 	StepData *data;
522 	gchar *path;
523 
524 	path = g_strconcat (test_prefix, test_path, NULL);
525 	data = step_test_new_internal (path, locale, store_vcards, empty_book);
526 	g_free (path);
527 
528 	return data;
529 }
530 
531 StepData *
step_test_new_full(const gchar * test_prefix,const gchar * test_path,const gchar * locale,gboolean store_vcards,gboolean empty_book,EBookCursorSortType sort_type)532 step_test_new_full (const gchar *test_prefix,
533                     const gchar *test_path,
534                     const gchar *locale,
535                     gboolean store_vcards,
536                     gboolean empty_book,
537                     EBookCursorSortType sort_type)
538 {
539 	StepData *data;
540 	gchar *path;
541 
542 	path = g_strconcat (test_prefix, test_path, NULL);
543 	data = step_test_new_internal (path, locale, store_vcards, empty_book);
544 	data->parent.sort_type = sort_type;
545 	g_free (path);
546 
547 	return data;
548 }
549 
550 static void
test_cursor_move_teardown(EbSqlCursorFixture * fixture,gconstpointer user_data)551 test_cursor_move_teardown (EbSqlCursorFixture *fixture,
552                            gconstpointer user_data)
553 {
554 	StepData *data = (StepData *) user_data;
555 
556 	e_sqlite_cursor_fixture_teardown (fixture, user_data);
557 	step_test_free (data);
558 }
559 
560 static void
assert_step(EbSqlCursorFixture * fixture,StepData * data,StepAssertion * assertion,GSList * results,gint n_results,gboolean expect_results)561 assert_step (EbSqlCursorFixture *fixture,
562              StepData *data,
563              StepAssertion *assertion,
564              GSList *results,
565              gint n_results,
566              gboolean expect_results)
567 {
568 	GSList *uids = NULL;
569 	gint i, expected = 0;
570 
571 	/* Count the number of really expected results */
572 	for (i = 0; i < ABS (assertion->count); i++) {
573 		gint index = assertion->expected[i];
574 
575 		if (index < 0)
576 			break;
577 
578 		expected++;
579 	}
580 
581 	g_assert_cmpint (n_results, ==, expected);
582 	if (!expect_results) {
583 		g_assert_cmpint (g_slist_length (results), ==, 0);
584 		return;
585 	}
586 
587 	/* Assert the exact amount of requested results */
588 	g_assert_cmpint (g_slist_length (results), ==, expected);
589 
590 #if DEBUG_FIXTURE
591 	g_print (
592 		"%s: Constructing expected result list for a fetch of %d: ",
593 		data->path, assertion->count);
594 #endif
595 	for (i = 0; i < ABS (assertion->count); i++) {
596 		gint index = assertion->expected[i];
597 		gchar *uid;
598 
599 		if (index < 0)
600 			break;
601 
602 		uid = (gchar *) e_contact_get_const (fixture->contacts[index], E_CONTACT_UID);
603 		uids = g_slist_append (uids, uid);
604 
605 #if DEBUG_FIXTURE
606 		g_print ("%s ", uid);
607 #endif
608 
609 	}
610 #if DEBUG_FIXTURE
611 	g_print ("\n");
612 #endif
613 
614 	assert_contacts_order_slist (results, uids);
615 	g_slist_free (uids);
616 }
617 
618 static void
test_step(EbSqlCursorFixture * fixture,gconstpointer user_data)619 test_step (EbSqlCursorFixture *fixture,
620            gconstpointer user_data)
621 {
622 	EbSqlFixture *base_fixture = (EbSqlFixture   *) fixture;
623 	StepData *data = (StepData *) user_data;
624 	GSList *results = NULL;
625 	GError *error = NULL;
626 	gint n_results;
627 	EbSqlCursorOrigin origin;
628 	GList *l;
629 	gboolean reset = TRUE;
630 
631 	for (l = data->assertions; l; l = l->next) {
632 		StepAssertion *assertion = l->data;
633 
634 		if (assertion->locale) {
635 			gint n_locale_changes = base_fixture->n_locale_changes;
636 
637 			if (!e_book_sqlite_set_locale (base_fixture->ebsql,
638 						       assertion->locale,
639 						       NULL, &error))
640 				g_error ("Failed to set locale: %s", error->message);
641 
642 			n_locale_changes = (base_fixture->n_locale_changes - n_locale_changes);
643 
644 			/* Only check for contact changes is phone numbers are supported,
645 			 * contact changes only happen because of e164 number interpretations.
646 			 */
647 			if (e_phone_number_is_supported () &&
648 			    assertion->count != -1 &&
649 			    assertion->count != n_locale_changes)
650 				g_error (
651 					"Expected %d e164 numbers to change, %d actually changed.",
652 					assertion->count, n_locale_changes);
653 
654 			reset = TRUE;
655 			continue;
656 		}
657 
658                /* For the first call to e_book_sqlite_cursor_step(),
659 		* or the first reset after locale change, set the origin accordingly.
660                 */
661 	       if (reset) {
662 		       if (assertion->count < 0)
663 			       origin = EBSQL_CURSOR_ORIGIN_END;
664 		       else
665 			       origin = EBSQL_CURSOR_ORIGIN_BEGIN;
666 
667 		       reset = FALSE;
668 	       } else
669 		       origin = EBSQL_CURSOR_ORIGIN_CURRENT;
670 
671 		/* Try only fetching the contacts but not moving the cursor */
672 		n_results = e_book_sqlite_cursor_step (
673 			base_fixture->ebsql,
674 			fixture->cursor,
675 			EBSQL_CURSOR_STEP_FETCH,
676 			origin,
677 			assertion->count,
678 			&results,
679 			NULL, &error);
680 		if (n_results < 0)
681 			g_error ("Error fetching cursor results: %s", error->message);
682 
683 		print_results (results);
684 		assert_step (fixture, data, assertion, results, n_results, TRUE);
685 		g_slist_foreach (results, (GFunc) e_book_sqlite_search_data_free, NULL);
686 		g_slist_free (results);
687 		results = NULL;
688 
689 		/* Do it again, this time only moving the cursor */
690 		n_results = e_book_sqlite_cursor_step (
691 			base_fixture->ebsql,
692 			fixture->cursor,
693 			EBSQL_CURSOR_STEP_MOVE,
694 			origin,
695 			assertion->count,
696 			&results,
697 			NULL, &error);
698 		if (n_results < 0)
699 			g_error ("Error fetching cursor results: %s", error->message);
700 
701 		print_results (results);
702 		assert_step (fixture, data, assertion, results, n_results, FALSE);
703 		g_slist_foreach (results, (GFunc) e_book_sqlite_search_data_free, NULL);
704 		g_slist_free (results);
705 		results = NULL;
706 	}
707 }
708 
709 static void
step_test_add_assertion_va_list(StepData * data,gint count,va_list args)710 step_test_add_assertion_va_list (StepData *data,
711                                  gint count,
712                                  va_list args)
713 {
714 	StepAssertion *assertion = g_slice_new0 (StepAssertion);
715 	gint expected, i;
716 
717 	assertion->count = count;
718 
719 #if DEBUG_FIXTURE
720 	g_print ("Adding assertion to test %d: %s\n", i + 1, data->path);
721 	g_print ("  Test will move by %d and expect: ", count);
722 #endif
723 	for (i = 0; i < ABS (count); i++) {
724 		expected = va_arg (args, gint);
725 
726 #if DEBUG_FIXTURE
727 		g_print ("%d ", expected);
728 #endif
729 		assertion->expected[i] = expected - 1;
730 	}
731 #if DEBUG_FIXTURE
732 	g_print ("\n");
733 #endif
734 
735 	data->assertions = g_list_append (data->assertions, assertion);
736 }
737 
738 /* A positive of negative 'count' value
739  * followed by ABS (count) UID indexes.
740  *
741  * The indexes start at 1 so that they
742  * are easier to match up with the chart
743  * in data-test-utils.h
744  */
745 void
step_test_add_assertion(StepData * data,gint count,...)746 step_test_add_assertion (StepData *data,
747                          gint count,
748                          ...)
749 {
750 	va_list args;
751 
752 	va_start (args, count);
753 	step_test_add_assertion_va_list (data, count, args);
754 	va_end (args);
755 }
756 
757 void
step_test_change_locale(StepData * data,const gchar * locale,gint expected_changes)758 step_test_change_locale (StepData *data,
759                          const gchar *locale,
760                          gint expected_changes)
761 {
762 	StepAssertion *assertion = g_slice_new0 (StepAssertion);
763 
764 	assertion->locale = g_strdup (locale);
765 	assertion->count = expected_changes;
766 	data->assertions = g_list_append (data->assertions, assertion);
767 }
768 
769 void
step_test_add(StepData * data,gboolean filtered)770 step_test_add (StepData *data,
771                gboolean filtered)
772 {
773 	data->filtered = filtered;
774 
775 	g_test_add (
776 		data->path, EbSqlCursorFixture, data,
777 		filtered ?
778 		e_sqlite_cursor_fixture_filtered_setup :
779 		e_sqlite_cursor_fixture_setup,
780 		test_step,
781 		test_cursor_move_teardown);
782 }
783 
784