1 /*
2  * Copyright 2016 Collabora Ltd.
3  *
4  * The geocode-glib library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * The geocode-glib library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
16  * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301  USA.
18  *
19  * Authors: Philip Withnall <philip.withnall@collabora.co.uk>
20  */
21 
22 #include "config.h"
23 
24 #include <geocode-glib/geocode-glib.h>
25 #include <gio/gio.h>
26 #include <glib.h>
27 #include <locale.h>
28 #include <stdlib.h>
29 
30 static void
place_list_free(GList * l)31 place_list_free (GList *l)
32 {
33 	g_list_free_full (l, g_object_unref);
34 }
35 
36 typedef GList PlaceList;
G_DEFINE_AUTOPTR_CLEANUP_FUNC(PlaceList,place_list_free)37 G_DEFINE_AUTOPTR_CLEANUP_FUNC (PlaceList, place_list_free)
38 
39 /* Checks the two #GeocodePlace lists are equal, and in the same order. */
40 static void
41 assert_place_list_equal (GList *a,
42                          GList *b)
43 {
44 	for (; a != NULL && b != NULL; a = a->next, b = b->next) {
45 		GeocodePlace *place_a, *place_b;
46 
47 		place_a = GEOCODE_PLACE (a->data);
48 		place_b = GEOCODE_PLACE (b->data);
49 
50 		g_assert (place_a != NULL);
51 		g_assert (place_b != NULL);
52 		g_assert_true (geocode_place_equal (place_a, place_b));
53 	}
54 
55 	g_assert (a == NULL);
56 	g_assert (b == NULL);
57 }
58 
59 static void
value_free(GValue * value)60 value_free (GValue *value)
61 {
62 	g_value_unset (value);
63 	g_free (value);
64 }
65 
66 static GHashTable *build_params (const gchar *first_key,
67                                  ...) G_GNUC_NULL_TERMINATED;
68 
69 /* Convenience method taking a varargs list of key–value pairs (all of which
70  * must be static strings) and returning them as a #GHashTable mapping strings
71  * to #GValues. */
72 static GHashTable *
build_params(const gchar * first_key,...)73 build_params (const gchar *first_key,
74               ...)
75 {
76 	g_autoptr (GHashTable) params = NULL;
77 	va_list ap;
78 	const gchar *key, *value_str;
79 
80 	params = g_hash_table_new_full (g_str_hash, g_str_equal,
81 	                                NULL, (GDestroyNotify) value_free);
82 
83 	va_start (ap, first_key);
84 	for (key = first_key, value_str = va_arg (ap, const gchar *);
85 	     key != NULL;
86 	     key = va_arg (ap, const gchar *),
87 	     value_str = va_arg (ap, const gchar *)) {
88 		GValue *value;
89 
90 		value = g_new0 (GValue, 1);
91 		g_value_init (value, G_TYPE_STRING);
92 		g_value_set_static_string (value, value_str);
93 		g_hash_table_insert (params, (gpointer) key,
94 		                     g_steal_pointer (&value));
95 	}
96 
97 	va_end (ap);
98 
99 	return g_steal_pointer (&params);
100 }
101 
102 /* Variant of build_params() which expects the values in the varargs list to be
103  * #gdoubles rather than strings. */
104 static GHashTable *
build_double_params(const gchar * first_key,...)105 build_double_params (const gchar *first_key,
106                      ...)
107 {
108 	g_autoptr (GHashTable) params = NULL;
109 	va_list ap;
110 	const gchar *key;
111 	gdouble value_double;
112 
113 	params = g_hash_table_new_full (g_str_hash, g_str_equal,
114 	                                NULL, (GDestroyNotify) value_free);
115 
116 	va_start (ap, first_key);
117 	for (key = first_key, value_double = va_arg (ap, gdouble);
118 	     key != NULL;
119 	     key = va_arg (ap, const gchar *),
120 	     value_double = va_arg (ap, gdouble)) {
121 		GValue *value;
122 
123 		value = g_new0 (GValue, 1);
124 		g_value_init (value, G_TYPE_DOUBLE);
125 		g_value_set_double (value, value_double);
126 		g_hash_table_insert (params, (gpointer) key,
127 		                     g_steal_pointer (&value));
128 	}
129 
130 	va_end (ap);
131 
132 	return g_steal_pointer (&params);
133 }
134 
135 /* Test that a #GeocodeForward query with a single result from the mock backend
136  * works. */
137 static void
test_forward_single_result(void)138 test_forward_single_result (void)
139 {
140 	g_autoptr (GeocodeForward) forward = NULL;
141 	g_autoptr (GeocodeMockBackend) backend = NULL;
142 	g_autoptr (GHashTable) params = NULL;
143 	g_autoptr (PlaceList) results = NULL;
144 	g_autoptr (PlaceList) expected_results = NULL;
145 	g_autoptr (GError) error = NULL;
146 	g_autoptr (GeocodePlace) expected_place = NULL;
147 	g_autoptr (GeocodeLocation) expected_location = NULL;
148 	GPtrArray *query_log;  /* (element-type GeocodeMockBackendQuery) */
149 
150 	backend = geocode_mock_backend_new ();
151 
152 	forward = geocode_forward_new_for_string ("Bullpot Farm");
153 	geocode_forward_set_backend (forward, GEOCODE_BACKEND (backend));
154 
155 	/* Build the set of parameters the mock backend expects to receive from
156 	 * the #GeocodeForward instance. */
157 	params = build_params ("location", "Bullpot Farm", NULL);
158 
159 	/* Build the set of results the mock backend should return. */
160 	expected_location = geocode_location_new_with_description (
161 	    54.22759825, -2.51857179181113, 5.0,
162 	    "Bullpot Farm, Fell Road, South Lakeland, Cumbria, "
163 	    "North West England, England, United Kingdom");
164 	expected_place = geocode_place_new_with_location (
165 	    "Bullpot Farm", GEOCODE_PLACE_TYPE_BUILDING, expected_location);
166 	expected_results = g_list_prepend (expected_results,
167 	                                   g_steal_pointer (&expected_place));
168 
169 	geocode_mock_backend_add_forward_result (backend, params,
170 	                                         expected_results, NULL);
171 
172 	/* Do the search. */
173 	results = geocode_forward_search (forward, &error);
174 
175 	g_assert_no_error (error);
176 	assert_place_list_equal (results, expected_results);
177 
178 	query_log = geocode_mock_backend_get_query_log (backend);
179 	g_assert_cmpuint (query_log->len, ==, 1);
180 }
181 
182 /* Test that a #GeocodeForward query with multiple results from the mock backend
183  * works. */
184 static void
test_forward_multiple_results(void)185 test_forward_multiple_results (void)
186 {
187 	g_autoptr (GeocodeForward) forward = NULL;
188 	g_autoptr (GeocodeMockBackend) backend = NULL;
189 	g_autoptr (GHashTable) params = NULL;
190 	g_autoptr (PlaceList) results = NULL;
191 	g_autoptr (PlaceList) expected_results = NULL;
192 	g_autoptr (GError) error = NULL;
193 	g_autoptr (GeocodePlace) expected_place1 = NULL;
194 	g_autoptr (GeocodePlace) expected_place2 = NULL;
195 	g_autoptr (GeocodePlace) expected_place3 = NULL;
196 	g_autoptr (GeocodeLocation) expected_location1 = NULL;
197 	g_autoptr (GeocodeLocation) expected_location2 = NULL;
198 	g_autoptr (GeocodeLocation) expected_location3 = NULL;
199 	GPtrArray *query_log;  /* (element-type GeocodeMockBackendQuery) */
200 
201 	backend = geocode_mock_backend_new ();
202 
203 	/* ‘Y Foel’ is Welsh for ‘the bald hill’. Those who have visited Wales
204 	 * will know there are quite a few such hills. */
205 	forward = geocode_forward_new_for_string ("Y Foel");
206 	geocode_forward_set_backend (forward, GEOCODE_BACKEND (backend));
207 
208 	/* Build the set of parameters the mock backend expects to receive from
209 	 * the #GeocodeForward instance. */
210 	params = build_params ("location", "Y Foel", NULL);
211 
212 	/* Build the set of results the mock backend should return. */
213 	expected_location1 = geocode_location_new (53.0309637, -4.3126653, 50.0);
214 	expected_place1 = geocode_place_new_with_location (
215 	    "Foel", GEOCODE_PLACE_TYPE_LAND_FEATURE, expected_location1);
216 	expected_results = g_list_prepend (expected_results,
217 	                                   g_steal_pointer (&expected_place1));
218 
219 	expected_location2 = geocode_location_new (52.9867051, -4.2023085, 50.0);
220 	expected_place2 = geocode_place_new_with_location (
221 	    "Y Foel", GEOCODE_PLACE_TYPE_LAND_FEATURE, expected_location2);
222 	expected_results = g_list_prepend (expected_results,
223 	                                   g_steal_pointer (&expected_place2));
224 
225 	expected_location3 = geocode_location_new (52.4456769, -3.4452951, 50.0);
226 	expected_place3 = geocode_place_new_with_location (
227 	    "Y Foel", GEOCODE_PLACE_TYPE_LAND_FEATURE, expected_location3);
228 	expected_results = g_list_prepend (expected_results,
229 	                                   g_steal_pointer (&expected_place3));
230 
231 	geocode_mock_backend_add_forward_result (backend, params,
232 	                                         expected_results, NULL);
233 
234 	/* Do the search. */
235 	results = geocode_forward_search (forward, &error);
236 
237 	g_assert_no_error (error);
238 	assert_place_list_equal (results, expected_results);
239 
240 	query_log = geocode_mock_backend_get_query_log (backend);
241 	g_assert_cmpuint (query_log->len, ==, 1);
242 }
243 
244 /* Test that a #GeocodeForward query with no results (but no error) from the
245  * mock backend works. */
246 static void
test_forward_no_results(void)247 test_forward_no_results (void)
248 {
249 	g_autoptr (GeocodeForward) forward = NULL;
250 	g_autoptr (GeocodeMockBackend) backend = NULL;
251 	g_autoptr (GHashTable) params = NULL;
252 	g_autoptr (PlaceList) results = NULL;
253 	g_autoptr (GError) error = NULL;
254 	const GError expected_error = {
255 	    GEOCODE_ERROR, GEOCODE_ERROR_NO_MATCHES,
256 	    (gchar *) "No matches found for request"
257 	};
258 	GPtrArray *query_log;  /* (element-type GeocodeMockBackendQuery) */
259 
260 	backend = geocode_mock_backend_new ();
261 
262 	forward = geocode_forward_new_for_string ("Reallydoesnotexist");
263 	geocode_forward_set_backend (forward, GEOCODE_BACKEND (backend));
264 
265 	/* Build the set of parameters the mock backend expects to receive from
266 	 * the #GeocodeForward instance. */
267 	params = build_params ("location", "Reallydoesnotexist", NULL);
268 
269 	geocode_mock_backend_add_forward_result (backend, params,
270 	                                         NULL  /* expected results */,
271 	                                         &expected_error);
272 
273 	/* Do the search. */
274 	results = geocode_forward_search (forward, &error);
275 
276 	g_assert_error (error, expected_error.domain, expected_error.code);
277 	g_assert_null (results);
278 
279 	query_log = geocode_mock_backend_get_query_log (backend);
280 	g_assert_cmpuint (query_log->len, ==, 1);
281 }
282 
283 /* Test that a #GeocodeForward query correctly handles errors from the mock
284  * backend. */
285 static void
test_forward_error(void)286 test_forward_error (void)
287 {
288 	g_autoptr (GeocodeForward) forward = NULL;
289 	g_autoptr (GeocodeMockBackend) backend = NULL;
290 	g_autoptr (GHashTable) params = NULL;
291 	g_autoptr (PlaceList) results = NULL;
292 	g_autoptr (GError) error = NULL;
293 	const GError expected_error = {
294 	    GEOCODE_ERROR, GEOCODE_ERROR_INTERNAL_SERVER,
295 	    (gchar *) "Internal server error"
296 	};
297 	GPtrArray *query_log;  /* (element-type GeocodeMockBackendQuery) */
298 
299 	backend = geocode_mock_backend_new ();
300 
301 	forward = geocode_forward_new_for_string ("Paradise");
302 	geocode_forward_set_backend (forward, GEOCODE_BACKEND (backend));
303 
304 	/* Build the set of parameters the mock backend expects to receive from
305 	 * the #GeocodeForward instance. */
306 	params = build_params ("location", "Paradise", NULL);
307 
308 	geocode_mock_backend_add_forward_result (backend, params,
309 	                                         NULL  /* expected results */,
310 	                                         &expected_error);
311 
312 	/* Do the search. */
313 	results = geocode_forward_search (forward, &error);
314 
315 	g_assert_error (error, expected_error.domain, expected_error.code);
316 	g_assert_null (results);
317 
318 	query_log = geocode_mock_backend_get_query_log (backend);
319 	g_assert_cmpuint (query_log->len, ==, 1);
320 }
321 
322 /* Test that a #GeocodeForward query with lots of additional parameters and no
323  * results (but no error) from the mock backend works. */
324 static void
test_forward_with_params(void)325 test_forward_with_params (void)
326 {
327 	g_autoptr (GeocodeForward) forward = NULL;
328 	g_autoptr (GeocodeMockBackend) backend = NULL;
329 	g_autoptr (GHashTable) params = NULL;
330 	g_autoptr (PlaceList) results = NULL;
331 	g_autoptr (GError) error = NULL;
332 	const GError expected_error = {
333 	    GEOCODE_ERROR, GEOCODE_ERROR_INTERNAL_SERVER,
334 	    (gchar *) "Some complex server error"
335 	};
336 	GPtrArray *query_log;  /* (element-type GeocodeMockBackendQuery) */
337 
338 	/* Build the set of parameters the mock backend expects to receive from
339 	 * the #GeocodeForward instance. */
340 	params = build_params ("building", "Kett House",
341 	                       "street", "Station Road",
342 	                       "locality", "Cambridge",
343 	                       "postalcode", "CB12JH",
344 	                       "country", "Inglaterra",
345 	                       "uri", "https://collabora.com/",
346 	                       "language", "es",
347 	                       NULL);
348 
349 	backend = geocode_mock_backend_new ();
350 
351 	forward = geocode_forward_new_for_params (params);
352 	geocode_forward_set_backend (forward, GEOCODE_BACKEND (backend));
353 
354 	geocode_mock_backend_add_forward_result (backend, params,
355 	                                         NULL  /* expected results */,
356 	                                         &expected_error);
357 
358 	/* Do the search. */
359 	results = geocode_forward_search (forward, &error);
360 
361 	g_assert_error (error, expected_error.domain, expected_error.code);
362 	g_assert_null (results);
363 
364 	query_log = geocode_mock_backend_get_query_log (backend);
365 	g_assert_cmpuint (query_log->len, ==, 1);
366 }
367 
368 /* Test that a #GeocodeReverse query with a single result from the mock backend
369  * works. */
370 static void
test_reverse_single_result(void)371 test_reverse_single_result (void)
372 {
373 	g_autoptr (GeocodeReverse) reverse = NULL;
374 	g_autoptr (GeocodeMockBackend) backend = NULL;
375 	g_autoptr (GeocodeLocation) location = NULL;
376 	g_autoptr (GHashTable) params = NULL;
377 	g_autoptr (GeocodePlace) result = NULL;
378 	g_autoptr (PlaceList) expected_results = NULL;
379 	g_autoptr (GError) error = NULL;
380 	g_autoptr (GeocodePlace) expected_place = NULL;
381 	g_autoptr (GeocodeLocation) expected_location = NULL;
382 	GPtrArray *query_log;  /* (element-type GeocodeMockBackendQuery) */
383 
384 	backend = geocode_mock_backend_new ();
385 
386 	location = geocode_location_new (52.2127749, 0.0806149693681216, 10.0);
387 	reverse = geocode_reverse_new_for_location (location);
388 	geocode_reverse_set_backend (reverse, GEOCODE_BACKEND (backend));
389 
390 	/* Build the set of parameters the mock backend expects to receive from
391 	 * the #GeocodeReverse instance. */
392 	params = build_double_params ("lat", 52.2127749,
393 	                              "lon", 0.0806149693681216,
394 	                              NULL);
395 
396 	/* Build the set of results the mock backend should return. */
397 	expected_location = geocode_location_new_with_description (
398 	    52.2127749, 0.0806149693681216, 10.0, "British Antarctic Survey");
399 	expected_place = geocode_place_new_with_location (
400 	    "British Antarctic Survey", GEOCODE_PLACE_TYPE_BUILDING,
401 	    expected_location);
402 	expected_results = g_list_prepend (expected_results,
403 	                                   g_steal_pointer (&expected_place));
404 
405 	geocode_mock_backend_add_reverse_result (backend, params,
406 	                                         expected_results, NULL);
407 
408 	/* Do the search. */
409 	result = geocode_reverse_resolve (reverse, &error);
410 
411 	g_assert_no_error (error);
412 	g_assert_true (geocode_place_equal (result, expected_results->data));
413 
414 	query_log = geocode_mock_backend_get_query_log (backend);
415 	g_assert_cmpuint (query_log->len, ==, 1);
416 }
417 
418 /* Test that a #GeocodeReverse query with multiple results from the mock backend
419  * works. This has to be done by testing the backend directly, since
420  * #GeocodeReverse does not support multiple results. */
421 static void
test_reverse_multiple_results(void)422 test_reverse_multiple_results (void)
423 {
424 	g_autoptr (GeocodeMockBackend) backend = NULL;
425 	g_autoptr (GeocodeLocation) location = NULL;
426 	g_autoptr (GHashTable) params = NULL;
427 	g_autoptr (PlaceList) results = NULL;
428 	g_autoptr (PlaceList) expected_results = NULL;
429 	g_autoptr (GError) error = NULL;
430 	g_autoptr (GeocodePlace) expected_place1 = NULL;
431 	g_autoptr (GeocodePlace) expected_place2 = NULL;
432 	g_autoptr (GeocodeLocation) expected_location1 = NULL;
433 	g_autoptr (GeocodeLocation) expected_location2 = NULL;
434 	GPtrArray *query_log;  /* (element-type GeocodeMockBackendQuery) */
435 
436 	backend = geocode_mock_backend_new ();
437 
438 	location = geocode_location_new (51.507891226831774, -0.12454301118850708, 1.0);
439 
440 	/* Build the set of parameters the mock backend expects to receive from
441 	 * the #GeocodeBackend instance. */
442 	params = build_double_params ("lat", 51.507891226831774,
443 	                              "lon", -0.12454301118850708,
444 	                              NULL);
445 
446 	/* Build the set of results the mock backend should return. */
447 	expected_location1 = geocode_location_new_with_description (
448 	    52.2127749, 0.0806149693681216, 1.0, "Heaven");
449 	expected_place1 = geocode_place_new_with_location (
450 	    "Heaven, The Arches, London, England", GEOCODE_PLACE_TYPE_UNKNOWN,
451 	    expected_location1);
452 	expected_results = g_list_prepend (expected_results,
453 	                                   g_steal_pointer (&expected_place1));
454 
455 	expected_location2 = geocode_location_new_with_description (
456 	    51.5077409783118, -0.12424796819686891, 50.0, "Charing Cross Station");
457 	expected_place2 = geocode_place_new_with_location (
458 	    "Charing Cross Station, London, England",
459 	    GEOCODE_PLACE_TYPE_RAILWAY_STATION, expected_location2);
460 	expected_results = g_list_prepend (expected_results,
461 	                                   g_steal_pointer (&expected_place2));
462 
463 	geocode_mock_backend_add_reverse_result (backend, params,
464 	                                         expected_results, NULL);
465 
466 	/* Do the search. */
467 	results = geocode_backend_reverse_resolve (GEOCODE_BACKEND (backend),
468 	                                           params, NULL, &error);
469 
470 	g_assert_no_error (error);
471 	assert_place_list_equal (results, expected_results);
472 
473 	query_log = geocode_mock_backend_get_query_log (backend);
474 	g_assert_cmpuint (query_log->len, ==, 1);
475 }
476 
477 /* Test that a #GeocodeReverse query with no results (but no error) from the
478  * mock backend works. */
479 static void
test_reverse_no_results(void)480 test_reverse_no_results (void)
481 {
482 	g_autoptr (GeocodeReverse) reverse = NULL;
483 	g_autoptr (GeocodeMockBackend) backend = NULL;
484 	g_autoptr (GeocodeLocation) location = NULL;
485 	g_autoptr (GHashTable) params = NULL;
486 	g_autoptr (GeocodePlace) result = NULL;
487 	g_autoptr (GError) error = NULL;
488 	const GError expected_error = {
489 	    GEOCODE_ERROR, GEOCODE_ERROR_NOT_SUPPORTED,
490 	    (gchar *) "Unable to geocode"
491 	};
492 	GPtrArray *query_log;  /* (element-type GeocodeMockBackendQuery) */
493 
494 	backend = geocode_mock_backend_new ();
495 
496 	location = geocode_location_new (45.4015357985572, -35.9033203125, 10.0);
497 	reverse = geocode_reverse_new_for_location (location);
498 	geocode_reverse_set_backend (reverse, GEOCODE_BACKEND (backend));
499 
500 	/* Build the set of parameters the mock backend expects to receive from
501 	 * the #GeocodeReverse instance. */
502 	params = build_double_params ("lat", 45.4015357985572,
503 	                              "lon", -35.9033203125,
504 	                              NULL);
505 
506 	geocode_mock_backend_add_reverse_result (backend, params,
507 	                                         NULL  /* expected results */,
508 	                                         &expected_error);
509 
510 	/* Do the search. */
511 	result = geocode_reverse_resolve (reverse, &error);
512 
513 	g_assert_error (error, expected_error.domain, expected_error.code);
514 	g_assert_null (result);
515 
516 	query_log = geocode_mock_backend_get_query_log (backend);
517 	g_assert_cmpuint (query_log->len, ==, 1);
518 }
519 
520 /* Test that a #GeocodeReverse query correctly handles errors from the mock
521  * backend. */
522 static void
test_reverse_error(void)523 test_reverse_error (void)
524 {
525 	g_autoptr (GeocodeReverse) reverse = NULL;
526 	g_autoptr (GeocodeMockBackend) backend = NULL;
527 	g_autoptr (GeocodeLocation) location = NULL;
528 	g_autoptr (GHashTable) params = NULL;
529 	g_autoptr (GeocodePlace) result = NULL;
530 	g_autoptr (GError) error = NULL;
531 	const GError expected_error = {
532 	    GEOCODE_ERROR, GEOCODE_ERROR_INTERNAL_SERVER,
533 	    (gchar *) "Internal server error"
534 	};
535 	GPtrArray *query_log;  /* (element-type GeocodeMockBackendQuery) */
536 
537 	backend = geocode_mock_backend_new ();
538 
539 	location = geocode_location_new (45.4015357985572, -35.9033203125, 10.0);
540 	reverse = geocode_reverse_new_for_location (location);
541 	geocode_reverse_set_backend (reverse, GEOCODE_BACKEND (backend));
542 
543 	/* Build the set of parameters the mock backend expects to receive from
544 	 * the #GeocodeReverse instance. */
545 	params = build_double_params ("lat", 45.4015357985572,
546 	                              "lon", -35.9033203125,
547 	                              NULL);
548 
549 	geocode_mock_backend_add_reverse_result (backend, params,
550 	                                         NULL  /* expected results */,
551 	                                         &expected_error);
552 
553 	/* Do the search. */
554 	result = geocode_reverse_resolve (reverse, &error);
555 
556 	g_assert_error (error, expected_error.domain, expected_error.code);
557 	g_assert_null (result);
558 
559 	query_log = geocode_mock_backend_get_query_log (backend);
560 	g_assert_cmpuint (query_log->len, ==, 1);
561 }
562 
563 /* Test that the query log and clear functionality on the backend works. */
564 static void
test_clear(void)565 test_clear (void)
566 {
567 	g_autoptr (GeocodeForward) forward = NULL;
568 	g_autoptr (GeocodeMockBackend) backend = NULL;
569 	g_autoptr (GHashTable) params = NULL;
570 	g_autoptr (PlaceList) results = NULL;
571 	g_autoptr (PlaceList) expected_results = NULL;
572 	g_autoptr (GError) error = NULL;
573 	g_autoptr (GeocodePlace) expected_place = NULL;
574 	g_autoptr (GeocodeLocation) expected_location = NULL;
575 	GPtrArray *query_log;  /* (element-type GeocodeMockBackendQuery) */
576 	const GeocodeMockBackendQuery *query;
577 
578 	backend = geocode_mock_backend_new ();
579 
580 	forward = geocode_forward_new_for_string ("Bullpot Farm");
581 	geocode_forward_set_backend (forward, GEOCODE_BACKEND (backend));
582 
583 	/* Build the set of parameters the mock backend expects to receive from
584 	 * the #GeocodeForward instance. */
585 	params = build_params ("location", "Bullpot Farm", NULL);
586 
587 	/* Build the set of results the mock backend should return. */
588 	expected_location = geocode_location_new_with_description (
589 	    54.22759825, -2.51857179181113, 5.0,
590 	    "Bullpot Farm, Fell Road, South Lakeland, Cumbria, "
591 	    "North West England, England, United Kingdom");
592 	expected_place = geocode_place_new_with_location (
593 	    "Bullpot Farm", GEOCODE_PLACE_TYPE_BUILDING, expected_location);
594 	expected_results = g_list_prepend (expected_results,
595 	                                   g_steal_pointer (&expected_place));
596 
597 	geocode_mock_backend_add_forward_result (backend, params,
598 	                                         expected_results, NULL);
599 
600 	/* Do the search. */
601 	results = geocode_forward_search (forward, &error);
602 
603 	g_assert_no_error (error);
604 	assert_place_list_equal (results, expected_results);
605 
606 	query_log = geocode_mock_backend_get_query_log (backend);
607 	g_assert_cmpuint (query_log->len, ==, 1);
608 
609 	query = (const GeocodeMockBackendQuery *) query_log->pdata[0];
610 	g_assert_cmpuint (g_hash_table_size (query->params), ==, 1);
611 	g_assert_true (query->is_forward);
612 	assert_place_list_equal (query->results, expected_results);
613 	g_assert_null (query->error);
614 
615 	/* Try clearing the backend then try another search. */
616 	geocode_mock_backend_clear (backend);
617 
618 	query_log = geocode_mock_backend_get_query_log (backend);
619 	g_assert_cmpuint (query_log->len, ==, 0);
620 
621 	results = geocode_forward_search (forward, &error);
622 	g_assert_null (results);
623 	g_assert_error (error, GEOCODE_ERROR, GEOCODE_ERROR_NO_MATCHES);
624 }
625 
626 int
main(int argc,char ** argv)627 main (int argc, char **argv)
628 {
629 	setlocale (LC_ALL, "");
630 	g_test_init (&argc, &argv, NULL);
631 	g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=");
632 
633 	g_test_add_func ("/mock-backend/forward/single-result",
634 	                 test_forward_single_result);
635 	g_test_add_func ("/mock-backend/forward/multiple-results",
636 	                 test_forward_multiple_results);
637 	g_test_add_func ("/mock-backend/forward/no-results",
638 	                 test_forward_no_results);
639 	g_test_add_func ("/mock-backend/forward/error", test_forward_error);
640 	g_test_add_func ("/mock-backend/forward/with-params",
641 	                 test_forward_with_params);
642 
643 	g_test_add_func ("/mock-backend/reverse-single-result",
644 	                 test_reverse_single_result);
645 	g_test_add_func ("/mock-backend/reverse-multiple-results",
646 	                 test_reverse_multiple_results);
647 	g_test_add_func ("/mock-backend/reverse-no-results",
648 	                 test_reverse_no_results);
649 	g_test_add_func ("/mock-backend/reverse-error", test_reverse_error);
650 
651 	g_test_add_func ("/mock-backend/clear", test_clear);
652 
653 	return g_test_run ();
654 }
655 
656