1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2012,2013 Intel Corporation
4  *
5  * This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Mathias Hasselmann <mathias@openismus.com>
18  */
19 
20 #include "evolution-data-server-config.h"
21 
22 #include <libebook-contacts/libebook-contacts.h>
23 #include <locale.h>
24 
25 /* Pick a locale category to set and test. */
26 #ifdef LC_ADDRESS
27 /* LC_ADDRESS is a GNU extension. */
28 #define CATEGORY LC_ADDRESS
29 #else
30 /* Mimic the fallback branch in EBookQuery. */
31 #ifdef G_OS_WIN32
32 #ifndef LC_MESSAGES
33 #define LC_MESSAGES LC_CTYPE
34 #endif
35 #endif
36 #define CATEGORY LC_MESSAGES
37 #endif /* LC_ADDRESS */
38 
39 static const gchar *match_candidates[] = {
40 	"not-a-number",
41 	"+1-617-4663489", "617-4663489", "4663489",
42 	"+1.408.845.5246", "4088455246", "8455246",
43 	"+1-857-4663489"
44 };
45 
46 #ifdef ENABLE_PHONENUMBER
47 static const EPhoneNumberMatch expected_matches[] = {
48 	/* not a number */
49 	E_PHONE_NUMBER_MATCH_NONE,
50 	E_PHONE_NUMBER_MATCH_NONE,
51 	E_PHONE_NUMBER_MATCH_NONE,
52 	E_PHONE_NUMBER_MATCH_NONE,
53 	E_PHONE_NUMBER_MATCH_NONE,
54 	E_PHONE_NUMBER_MATCH_NONE,
55 	E_PHONE_NUMBER_MATCH_NONE,
56 	E_PHONE_NUMBER_MATCH_NONE,
57 
58 	/* +1-617-4663489 */
59 	E_PHONE_NUMBER_MATCH_NONE,
60 	E_PHONE_NUMBER_MATCH_EXACT,
61 	E_PHONE_NUMBER_MATCH_NATIONAL,
62 	E_PHONE_NUMBER_MATCH_SHORT,
63 	E_PHONE_NUMBER_MATCH_NONE,
64 	E_PHONE_NUMBER_MATCH_NONE,
65 	E_PHONE_NUMBER_MATCH_NONE,
66 	E_PHONE_NUMBER_MATCH_NONE,
67 
68 	/* 617-4663489 */
69 	E_PHONE_NUMBER_MATCH_NONE,
70 	E_PHONE_NUMBER_MATCH_NATIONAL,
71 	E_PHONE_NUMBER_MATCH_NATIONAL,
72 	E_PHONE_NUMBER_MATCH_SHORT,
73 	E_PHONE_NUMBER_MATCH_NONE,
74 	E_PHONE_NUMBER_MATCH_NONE,
75 	E_PHONE_NUMBER_MATCH_NONE,
76 	E_PHONE_NUMBER_MATCH_NONE,
77 
78 	/* 4663489 */
79 	E_PHONE_NUMBER_MATCH_NONE,
80 	E_PHONE_NUMBER_MATCH_SHORT,
81 	E_PHONE_NUMBER_MATCH_SHORT,
82 	E_PHONE_NUMBER_MATCH_NATIONAL, /* XXX - Google, really? I'd expect a full match here. */
83 	E_PHONE_NUMBER_MATCH_NONE,
84 	E_PHONE_NUMBER_MATCH_NONE,
85 	E_PHONE_NUMBER_MATCH_NONE,
86 	E_PHONE_NUMBER_MATCH_SHORT,
87 
88 	/* +1.408.845.5246 */
89 	E_PHONE_NUMBER_MATCH_NONE,
90 	E_PHONE_NUMBER_MATCH_NONE,
91 	E_PHONE_NUMBER_MATCH_NONE,
92 	E_PHONE_NUMBER_MATCH_NONE,
93 	E_PHONE_NUMBER_MATCH_EXACT,
94 	E_PHONE_NUMBER_MATCH_NATIONAL,
95 	E_PHONE_NUMBER_MATCH_SHORT,
96 	E_PHONE_NUMBER_MATCH_NONE,
97 
98 	/* 4088455246 */
99 	E_PHONE_NUMBER_MATCH_NONE,
100 	E_PHONE_NUMBER_MATCH_NONE,
101 	E_PHONE_NUMBER_MATCH_NONE,
102 	E_PHONE_NUMBER_MATCH_NONE,
103 	E_PHONE_NUMBER_MATCH_NATIONAL,
104 	E_PHONE_NUMBER_MATCH_NATIONAL,
105 	E_PHONE_NUMBER_MATCH_SHORT,
106 	E_PHONE_NUMBER_MATCH_NONE,
107 
108 	/* 8455246 */
109 	E_PHONE_NUMBER_MATCH_NONE,
110 	E_PHONE_NUMBER_MATCH_NONE,
111 	E_PHONE_NUMBER_MATCH_NONE,
112 	E_PHONE_NUMBER_MATCH_NONE,
113 	E_PHONE_NUMBER_MATCH_SHORT,
114 	E_PHONE_NUMBER_MATCH_SHORT,
115 	E_PHONE_NUMBER_MATCH_NATIONAL, /* XXX - Google, really?  I'd expect a full match here. */
116 	E_PHONE_NUMBER_MATCH_NONE,
117 
118 	/* +1-857-4663489 */
119 	E_PHONE_NUMBER_MATCH_NONE,
120 	E_PHONE_NUMBER_MATCH_NONE,
121 	E_PHONE_NUMBER_MATCH_NONE,
122 	E_PHONE_NUMBER_MATCH_SHORT,
123 	E_PHONE_NUMBER_MATCH_NONE,
124 	E_PHONE_NUMBER_MATCH_NONE,
125 	E_PHONE_NUMBER_MATCH_NONE,
126 	E_PHONE_NUMBER_MATCH_EXACT
127 };
128 #endif /* ENABLE_PHONENUMBER */
129 
130 typedef struct {
131 	gchar				*phone_number;
132 	gchar				*region_code;
133 	EPhoneNumberCountrySource	 country_source;
134 	gint				 country_code;
135 	gchar				*national_number;
136 	gchar				*formatted_numbers[4];
137 } ParseAndFormatData;
138 
139 static ParseAndFormatData *
parse_and_format_data_new(const gchar * phone_number,const gchar * region_code,EPhoneNumberCountrySource country_source,gint country_code,const gchar * national_number,const gchar * formatted_e164,const gchar * formatted_intl,const gchar * formatted_natl,const gchar * formatted_uri)140 parse_and_format_data_new (const gchar *phone_number,
141                            const gchar *region_code,
142                            EPhoneNumberCountrySource country_source,
143                            gint country_code,
144                            const gchar *national_number,
145                            const gchar *formatted_e164,
146                            const gchar *formatted_intl,
147                            const gchar *formatted_natl,
148                            const gchar *formatted_uri)
149 {
150 	ParseAndFormatData *test_data = g_slice_new0 (ParseAndFormatData);
151 
152 	test_data->phone_number = g_strdup (phone_number);
153 	test_data->region_code = g_strdup (region_code);
154 	test_data->country_source = country_source;
155 	test_data->country_code = country_code;
156 	test_data->national_number = g_strdup (national_number);
157 	test_data->formatted_numbers[0] = g_strdup (formatted_e164);
158 	test_data->formatted_numbers[1] = g_strdup (formatted_intl);
159 	test_data->formatted_numbers[2] = g_strdup (formatted_natl);
160 	test_data->formatted_numbers[3] = g_strdup (formatted_uri);
161 
162 	return test_data;
163 }
164 
165 static void
parse_and_format_data_free(gpointer data)166 parse_and_format_data_free (gpointer data)
167 {
168 	ParseAndFormatData *const test_data = data;
169 
170 	g_free (test_data->phone_number);
171 	g_free (test_data->region_code);
172 	g_free (test_data->national_number);
173 	g_free (test_data->formatted_numbers[0]);
174 	g_free (test_data->formatted_numbers[1]);
175 	g_free (test_data->formatted_numbers[2]);
176 	g_free (test_data->formatted_numbers[3]);
177 
178 	g_slice_free (ParseAndFormatData, test_data);
179 }
180 
181 static void
test_parse_and_format(gconstpointer data)182 test_parse_and_format (gconstpointer data)
183 {
184 	const ParseAndFormatData *const test_data = data;
185 	GError *error = NULL;
186 	EPhoneNumber *parsed;
187 
188 	parsed = e_phone_number_from_string (
189 		test_data->phone_number, test_data->region_code, &error);
190 
191 #ifdef ENABLE_PHONENUMBER
192 
193 	{
194 		EPhoneNumberCountrySource source;
195 		gchar *national;
196 		gint i;
197 
198 		g_assert_cmpint (
199 			e_phone_number_get_country_code (parsed, &source), ==,
200 			test_data->country_code);
201 		g_assert_cmpuint (source, ==, test_data->country_source);
202 
203 		national = e_phone_number_get_national_number (parsed);
204 		g_assert_cmpstr (national, ==, test_data->national_number);
205 		g_free (national);
206 
207 		g_assert (parsed != NULL);
208 		g_assert (error == NULL);
209 
210 		for (i = 0; i < G_N_ELEMENTS (test_data->formatted_numbers); ++i) {
211 			gchar *formatted = e_phone_number_to_string (parsed, i);
212 			g_assert (formatted != NULL);
213 			g_assert_cmpstr (formatted, ==, test_data->formatted_numbers[i]);
214 			g_free (formatted);
215 		}
216 
217 		e_phone_number_free (parsed);
218 	}
219 
220 #else /* ENABLE_PHONENUMBER */
221 
222 	g_assert (parsed == NULL);
223 	g_assert (error != NULL);
224 	g_assert (error->domain == E_PHONE_NUMBER_ERROR);
225 	g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
226 	g_assert (error->message != NULL);
227 
228 #endif /* ENABLE_PHONENUMBER */
229 
230 	g_clear_error (&error);
231 }
232 
233 static void
test_parse_bad_number(void)234 test_parse_bad_number (void)
235 {
236 	GError *error = NULL;
237 	EPhoneNumber *parsed;
238 
239 	parsed = e_phone_number_from_string ("+1-NOT-A-NUMBER", "US", &error);
240 
241 	g_assert (parsed == NULL);
242 	g_assert (error != NULL);
243 	g_assert (error->domain == E_PHONE_NUMBER_ERROR);
244 #ifdef ENABLE_PHONENUMBER
245 	g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_A_NUMBER);
246 #else /* ENABLE_PHONENUMBER */
247 	g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
248 #endif /* ENABLE_PHONENUMBER */
249 	g_assert (error->message != NULL);
250 
251 	g_clear_error (&error);
252 }
253 
254 static void
test_parse_auto_region(void)255 test_parse_auto_region (void)
256 {
257 	GError *error = NULL;
258 	EPhoneNumber *parsed;
259 
260 	parsed = e_phone_number_from_string ("212-5423789", NULL, &error);
261 
262 #ifdef ENABLE_PHONENUMBER
263 
264 	{
265 		EPhoneNumberCountrySource source;
266 		gchar *national;
267 		gchar *formatted;
268 
269 		g_assert (parsed != NULL);
270 		g_assert (error == NULL);
271 
272 		g_assert_cmpint (e_phone_number_get_country_code (parsed, &source), ==, 1);
273 		g_assert_cmpuint (source, ==, E_PHONE_NUMBER_COUNTRY_FROM_DEFAULT);
274 
275 		national = e_phone_number_get_national_number (parsed);
276 		g_assert_cmpstr (national, ==, "2125423789");
277 		g_free (national);
278 
279 		formatted = e_phone_number_to_string (parsed, E_PHONE_NUMBER_FORMAT_E164);
280 		g_assert_cmpstr (formatted, ==, "+12125423789");
281 		g_free (formatted);
282 
283 		e_phone_number_free (parsed);
284 	}
285 
286 #else /* ENABLE_PHONENUMBER */
287 
288 	g_assert (parsed == NULL);
289 	g_assert (error != NULL);
290 	g_assert (error->domain == E_PHONE_NUMBER_ERROR);
291 	g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
292 	g_assert (error->message != NULL);
293 	g_clear_error (&error);
294 
295 #endif /* ENABLE_PHONENUMBER */
296 }
297 
298 static void
test_compare_numbers(gconstpointer data)299 test_compare_numbers (gconstpointer data)
300 {
301 	const size_t n = GPOINTER_TO_UINT (data);
302 	const size_t i = n % G_N_ELEMENTS (match_candidates);
303 	const size_t j = n / G_N_ELEMENTS (match_candidates);
304 
305 #ifdef ENABLE_PHONENUMBER
306 	const gboolean error_expected = !(i && j);
307 #else /* ENABLE_PHONENUMBER */
308 	const gboolean error_expected = TRUE;
309 #endif /* ENABLE_PHONENUMBER */
310 
311 	EPhoneNumberMatch actual_match;
312 	GError *error = NULL;
313 
314 	actual_match = e_phone_number_compare_strings (
315 		match_candidates[i],
316 		match_candidates[j],
317 		&error);
318 
319 #ifdef ENABLE_PHONENUMBER
320 	g_assert_cmpuint (actual_match, ==, expected_matches[n]);
321 #else /* ENABLE_PHONENUMBER */
322 	g_assert_cmpuint (actual_match, ==, E_PHONE_NUMBER_MATCH_NONE);
323 #endif /* ENABLE_PHONENUMBER */
324 
325 	if (!error_expected) {
326 		g_assert (error == NULL);
327 	} else {
328 		g_assert (error != NULL);
329 		g_assert (error->domain == E_PHONE_NUMBER_ERROR);
330 #ifdef ENABLE_PHONENUMBER
331 		g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_A_NUMBER);
332 #else /* ENABLE_PHONENUMBER */
333 		g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
334 #endif /* ENABLE_PHONENUMBER */
335 		g_assert (error->message != NULL);
336 
337 		g_clear_error (&error);
338 	}
339 }
340 
341 static void
test_supported(void)342 test_supported (void)
343 {
344 #ifdef ENABLE_PHONENUMBER
345 	g_assert (e_phone_number_is_supported ());
346 #else /* ENABLE_PHONENUMBER */
347 	g_assert (!e_phone_number_is_supported ());
348 #endif /* ENABLE_PHONENUMBER */
349 }
350 
351 static void
test_country_code_for_region(void)352 test_country_code_for_region (void)
353 {
354 	GError *error = NULL;
355 	gint code;
356 
357 	g_assert_cmpstr (setlocale (CATEGORY, NULL), ==, "en_US.UTF-8");
358 
359 #ifdef ENABLE_PHONENUMBER
360 
361 	code = e_phone_number_get_country_code_for_region ("CH", &error);
362 	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
363 	g_assert_cmpint (code, ==, 41);
364 
365 	code = e_phone_number_get_country_code_for_region (NULL, &error);
366 	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
367 	g_assert_cmpint (code, ==, 1);
368 
369 	code = e_phone_number_get_country_code_for_region ("C", &error);
370 	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
371 	g_assert_cmpint (code, ==, 0);
372 
373 	code = e_phone_number_get_country_code_for_region ("", &error);
374 	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
375 	g_assert_cmpint (code, ==, 1);
376 
377 #else /* ENABLE_PHONENUMBER */
378 
379 	code = e_phone_number_get_country_code_for_region ("CH", &error);
380 
381 	g_assert (error != NULL);
382 	g_assert (error->domain == E_PHONE_NUMBER_ERROR);
383 	g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
384 	g_assert (error->message != NULL);
385 	g_assert_cmpint (code, ==, 0);
386 
387 #endif /* ENABLE_PHONENUMBER */
388 }
389 
390 static void
test_default_region(void)391 test_default_region (void)
392 {
393 	GError *error = NULL;
394 	gchar *country;
395 
396 	g_assert_cmpstr (setlocale (CATEGORY, NULL), ==, "en_US.UTF-8");
397 	country = e_phone_number_get_default_region (&error);
398 
399 #ifdef ENABLE_PHONENUMBER
400 
401 	g_assert_cmpstr (error ? error->message : NULL, ==, NULL);
402 	g_assert_cmpstr (country, ==, "US");
403 
404 #else /* ENABLE_PHONENUMBER */
405 
406 	g_assert (error != NULL);
407 	g_assert (error->domain == E_PHONE_NUMBER_ERROR);
408 	g_assert (error->code == E_PHONE_NUMBER_ERROR_NOT_IMPLEMENTED);
409 	g_assert (error->message != NULL);
410 	g_assert_cmpstr (country, ==, NULL);
411 
412 #endif /* ENABLE_PHONENUMBER */
413 
414 	g_free (country);
415 }
416 
417 gint
main(gint argc,gchar ** argv)418 main (gint argc,
419       gchar **argv)
420 {
421 	size_t i, j;
422 
423 	if (setlocale (LC_ALL, "en_US.UTF-8") == NULL) {
424 		g_message ("Failed to set locale to en_US.UTF-8");
425 		g_message ("Skipping all /ebook-phone-number/* tests");
426 		return 0;
427 	}
428 
429 	g_test_init (&argc, &argv, NULL);
430 	g_test_bug_base ("http://bugzilla.gnome.org/");
431 
432 	g_test_add_func (
433 		"/ebook-phone-number/supported",
434 		test_supported);
435 
436 	g_test_add_data_func_full (
437 		"/ebook-phone-number/parse-and-format/i164",
438 		parse_and_format_data_new (
439 			"+493011223344", NULL,
440 			E_PHONE_NUMBER_COUNTRY_FROM_FQTN,
441 			49, "3011223344",
442 			"+493011223344",
443 			"+49 30 11223344",
444 			"030 11223344",
445 			"tel:+49-30-11223344"),
446 		test_parse_and_format,
447 		parse_and_format_data_free);
448 	g_test_add_data_func_full (
449 		"/ebook-phone-number/parse-and-format/national",
450 		parse_and_format_data_new (
451 			"(030) 22334-455", "DE",
452 			E_PHONE_NUMBER_COUNTRY_FROM_DEFAULT,
453 			49, "3022334455",
454 			"+493022334455",
455 			"+49 30 22334455",
456 			"030 22334455",
457 			"tel:+49-30-22334455"),
458 		test_parse_and_format,
459 		parse_and_format_data_free);
460 	g_test_add_data_func_full (
461 		"/ebook-phone-number/parse-and-format/national2",
462 		parse_and_format_data_new (
463 			"0049 (30) 22334-455", "DE",
464 			E_PHONE_NUMBER_COUNTRY_FROM_IDD,
465 			49, "3022334455",
466 			"+493022334455",
467 			"+49 30 22334455",
468 			"030 22334455",
469 			"tel:+49-30-22334455"),
470 		test_parse_and_format,
471 		parse_and_format_data_free);
472 	g_test_add_data_func_full (
473 		"/ebook-phone-number/parse-and-format/international",
474 		parse_and_format_data_new (
475 			"+1 212 33445566", NULL,
476 			E_PHONE_NUMBER_COUNTRY_FROM_FQTN,
477 			1, "21233445566",
478 			"+121233445566",
479 			"+1 21233445566",
480 			"21233445566",
481 			"tel:+1-21233445566"),
482 		test_parse_and_format,
483 		parse_and_format_data_free);
484 	g_test_add_data_func_full (
485 		"/ebook-phone-number/parse-and-format/rfc3966",
486 		parse_and_format_data_new (
487 			"tel:+358-71-44556677", NULL,
488 			E_PHONE_NUMBER_COUNTRY_FROM_FQTN,
489 			358, "7144556677",
490 			"+3587144556677",
491 			"+358 71 44556677",
492 			"071 44556677",
493 			"tel:+358-71-44556677"),
494 		test_parse_and_format,
495 		parse_and_format_data_free);
496 
497 	g_test_add_func (
498 		"/ebook-phone-number/parse-and-format/bad-number",
499 		test_parse_bad_number);
500 
501 	g_test_add_func (
502 		"/ebook-phone-number/parse-and-format/auto-region",
503 		test_parse_auto_region);
504 
505 	#ifdef ENABLE_PHONENUMBER
506 	g_assert_cmpint (
507 		G_N_ELEMENTS (match_candidates) * G_N_ELEMENTS (match_candidates),
508 		==, G_N_ELEMENTS (expected_matches));
509 	#endif
510 
511 	for (i = 0; i < G_N_ELEMENTS (match_candidates); ++i) {
512 		for (j = 0; j < G_N_ELEMENTS (match_candidates); ++j) {
513 			const size_t n = j + i * G_N_ELEMENTS (match_candidates);
514 			gchar *path = g_strdup_printf (
515 				"/ebook-phone-number/compare/%s/%s",
516 				match_candidates[i], match_candidates[j]);
517 
518 			g_test_add_data_func (
519 				path,
520 				GUINT_TO_POINTER (n),
521 				test_compare_numbers);
522 			g_free (path);
523 		}
524 	}
525 
526 	g_test_add_func (
527 		"/ebook-phone-number/country-code/for-region",
528 		test_country_code_for_region);
529 	g_test_add_func (
530 		"/ebook-phone-number/country-code/default-region",
531 		test_default_region);
532 
533 	return g_test_run ();
534 }
535