1 | /* GLib testing framework examples and tests |
2 | * |
3 | * Copyright (C) 2011 Collabora Ltd. |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2.1 of the License, or (at your option) any later version. |
9 | * |
10 | * This library 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 GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General |
16 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
17 | * |
18 | * Author: Stef Walter <stefw@collobora.co.uk> |
19 | */ |
20 | |
21 | #include "config.h" |
22 | |
23 | #include <gio/gio.h> |
24 | |
25 | #include "gtesttlsbackend.h" |
26 | |
27 | static GPtrArray *fixtures = NULL; |
28 | |
29 | typedef struct { |
30 | /* Class virtual interaction methods */ |
31 | gpointer ask_password_func; |
32 | gpointer ask_password_async_func; |
33 | gpointer ask_password_finish_func; |
34 | gpointer request_certificate_func; |
35 | gpointer request_certificate_async_func; |
36 | gpointer request_certificate_finish_func; |
37 | |
38 | /* Expected results */ |
39 | GTlsInteractionResult result; |
40 | GQuark error_domain; |
41 | gint error_code; |
42 | const gchar *error_message; |
43 | } Fixture; |
44 | |
45 | typedef struct { |
46 | GTlsInteraction *interaction; |
47 | GTlsPassword *password; |
48 | GTlsConnection *connection; |
49 | GMainLoop *loop; |
50 | GThread *interaction_thread; |
51 | GThread *test_thread; |
52 | GThread *loop_thread; |
53 | const Fixture *fixture; |
54 | } Test; |
55 | |
56 | typedef struct { |
57 | GTlsInteraction parent; |
58 | Test *test; |
59 | } TestInteraction; |
60 | |
61 | typedef struct { |
62 | GTlsInteractionClass parent; |
63 | } TestInteractionClass; |
64 | |
65 | static GType test_interaction_get_type (void); |
66 | G_DEFINE_TYPE (TestInteraction, test_interaction, G_TYPE_TLS_INTERACTION) |
67 | |
68 | #define TEST_TYPE_INTERACTION (test_interaction_get_type ()) |
69 | #define TEST_INTERACTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TEST_TYPE_INTERACTION, TestInteraction)) |
70 | #define TEST_IS_INTERACTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TEST_TYPE_INTERACTION)) |
71 | |
72 | static void |
73 | test_interaction_init (TestInteraction *self) |
74 | { |
75 | |
76 | } |
77 | |
78 | static void |
79 | test_interaction_class_init (TestInteractionClass *klass) |
80 | { |
81 | /* By default no virtual methods */ |
82 | } |
83 | |
84 | static void |
85 | test_interaction_ask_password_async_success (GTlsInteraction *interaction, |
86 | GTlsPassword *password, |
87 | GCancellable *cancellable, |
88 | GAsyncReadyCallback callback, |
89 | gpointer user_data) |
90 | { |
91 | GTask *task; |
92 | TestInteraction *self; |
93 | |
94 | g_assert (TEST_IS_INTERACTION (interaction)); |
95 | self = TEST_INTERACTION (interaction); |
96 | |
97 | g_assert (g_thread_self () == self->test->interaction_thread); |
98 | |
99 | g_assert (G_IS_TLS_PASSWORD (password)); |
100 | g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
101 | |
102 | task = g_task_new (source_object: self, cancellable, callback, callback_data: user_data); |
103 | |
104 | /* Don't do this in real life. Include a null terminator for testing */ |
105 | g_tls_password_set_value (password, value: (const guchar *)"the password" , length: 13); |
106 | g_task_return_int (task, result: G_TLS_INTERACTION_HANDLED); |
107 | g_object_unref (object: task); |
108 | } |
109 | |
110 | |
111 | static GTlsInteractionResult |
112 | test_interaction_ask_password_finish_success (GTlsInteraction *interaction, |
113 | GAsyncResult *result, |
114 | GError **error) |
115 | { |
116 | TestInteraction *self; |
117 | |
118 | g_assert (TEST_IS_INTERACTION (interaction)); |
119 | self = TEST_INTERACTION (interaction); |
120 | |
121 | g_assert (g_thread_self () == self->test->interaction_thread); |
122 | |
123 | g_assert (g_task_is_valid (result, interaction)); |
124 | g_assert (error != NULL); |
125 | g_assert (*error == NULL); |
126 | |
127 | return g_task_propagate_int (G_TASK (result), error); |
128 | } |
129 | |
130 | static void |
131 | test_interaction_ask_password_async_failure (GTlsInteraction *interaction, |
132 | GTlsPassword *password, |
133 | GCancellable *cancellable, |
134 | GAsyncReadyCallback callback, |
135 | gpointer user_data) |
136 | { |
137 | GTask *task; |
138 | TestInteraction *self; |
139 | |
140 | g_assert (TEST_IS_INTERACTION (interaction)); |
141 | self = TEST_INTERACTION (interaction); |
142 | |
143 | g_assert (g_thread_self () == self->test->interaction_thread); |
144 | |
145 | g_assert (G_IS_TLS_PASSWORD (password)); |
146 | g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
147 | |
148 | task = g_task_new (source_object: self, cancellable, callback, callback_data: user_data); |
149 | |
150 | g_task_return_new_error (task, G_FILE_ERROR, code: G_FILE_ERROR_ACCES, format: "The message" ); |
151 | g_object_unref (object: task); |
152 | } |
153 | |
154 | static GTlsInteractionResult |
155 | test_interaction_ask_password_finish_failure (GTlsInteraction *interaction, |
156 | GAsyncResult *result, |
157 | GError **error) |
158 | { |
159 | TestInteraction *self; |
160 | |
161 | g_assert (TEST_IS_INTERACTION (interaction)); |
162 | self = TEST_INTERACTION (interaction); |
163 | |
164 | g_assert (g_thread_self () == self->test->interaction_thread); |
165 | |
166 | g_assert (g_task_is_valid (result, interaction)); |
167 | g_assert (error != NULL); |
168 | g_assert (*error == NULL); |
169 | |
170 | if (g_task_propagate_int (G_TASK (result), error) != -1) |
171 | g_assert_not_reached (); |
172 | |
173 | return G_TLS_INTERACTION_FAILED; |
174 | } |
175 | |
176 | |
177 | /* Return a copy of @str that is allocated in a silly way, to exercise |
178 | * custom free-functions. The returned pointer points to a copy of @str |
179 | * in a buffer of the form "BEFORE \0 str \0 AFTER". */ |
180 | static guchar * |
181 | special_dup (const char *str) |
182 | { |
183 | GString *buf = g_string_new (init: "BEFORE" ); |
184 | guchar *ret; |
185 | |
186 | g_string_append_c (buf, '\0'); |
187 | g_string_append (string: buf, val: str); |
188 | g_string_append_c (buf, '\0'); |
189 | g_string_append (string: buf, val: "AFTER" ); |
190 | ret = (guchar *) g_string_free (string: buf, FALSE); |
191 | return ret + strlen (s: "BEFORE" ) + 1; |
192 | } |
193 | |
194 | |
195 | /* Free a copy of @str that was made with special_dup(), after asserting |
196 | * that it has not been corrupted. */ |
197 | static void |
198 | special_free (gpointer p) |
199 | { |
200 | gchar *s = p; |
201 | gchar *buf = s - strlen (s: "BEFORE" ) - 1; |
202 | |
203 | g_assert_cmpstr (buf, ==, "BEFORE" ); |
204 | g_assert_cmpstr (s + strlen (s) + 1, ==, "AFTER" ); |
205 | g_free (mem: buf); |
206 | } |
207 | |
208 | |
209 | static GTlsInteractionResult |
210 | test_interaction_ask_password_sync_success (GTlsInteraction *interaction, |
211 | GTlsPassword *password, |
212 | GCancellable *cancellable, |
213 | GError **error) |
214 | { |
215 | TestInteraction *self; |
216 | const guchar *value; |
217 | gsize len; |
218 | |
219 | g_assert (TEST_IS_INTERACTION (interaction)); |
220 | self = TEST_INTERACTION (interaction); |
221 | |
222 | g_assert (g_thread_self () == self->test->interaction_thread); |
223 | |
224 | g_assert (G_IS_TLS_PASSWORD (password)); |
225 | g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
226 | g_assert (error != NULL); |
227 | g_assert (*error == NULL); |
228 | |
229 | /* Exercise different ways to set the value */ |
230 | g_tls_password_set_value (password, value: (const guchar *) "foo" , length: 4); |
231 | len = 0; |
232 | value = g_tls_password_get_value (password, length: &len); |
233 | g_assert_cmpmem (value, len, "foo" , 4); |
234 | |
235 | g_tls_password_set_value (password, value: (const guchar *) "bar" , length: -1); |
236 | len = 0; |
237 | value = g_tls_password_get_value (password, length: &len); |
238 | g_assert_cmpmem (value, len, "bar" , 3); |
239 | |
240 | g_tls_password_set_value_full (password, value: special_dup (str: "baa" ), length: 4, destroy: special_free); |
241 | len = 0; |
242 | value = g_tls_password_get_value (password, length: &len); |
243 | g_assert_cmpmem (value, len, "baa" , 4); |
244 | |
245 | g_tls_password_set_value_full (password, value: special_dup (str: "baz" ), length: -1, destroy: special_free); |
246 | len = 0; |
247 | value = g_tls_password_get_value (password, length: &len); |
248 | g_assert_cmpmem (value, len, "baz" , 3); |
249 | |
250 | /* Don't do this in real life. Include a null terminator for testing */ |
251 | g_tls_password_set_value (password, value: (const guchar *)"the password" , length: 13); |
252 | return G_TLS_INTERACTION_HANDLED; |
253 | } |
254 | |
255 | static GTlsInteractionResult |
256 | test_interaction_ask_password_sync_failure (GTlsInteraction *interaction, |
257 | GTlsPassword *password, |
258 | GCancellable *cancellable, |
259 | GError **error) |
260 | { |
261 | TestInteraction *self; |
262 | |
263 | g_assert (TEST_IS_INTERACTION (interaction)); |
264 | self = TEST_INTERACTION (interaction); |
265 | |
266 | g_assert (g_thread_self () == self->test->interaction_thread); |
267 | |
268 | g_assert (G_IS_TLS_PASSWORD (password)); |
269 | g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
270 | g_assert (error != NULL); |
271 | g_assert (*error == NULL); |
272 | |
273 | g_set_error (err: error, G_FILE_ERROR, code: G_FILE_ERROR_ACCES, format: "The message" ); |
274 | return G_TLS_INTERACTION_FAILED; |
275 | } |
276 | |
277 | static void |
278 | test_interaction_request_certificate_async_success (GTlsInteraction *interaction, |
279 | GTlsConnection *connection, |
280 | gint unused_flags, |
281 | GCancellable *cancellable, |
282 | GAsyncReadyCallback callback, |
283 | gpointer user_data) |
284 | { |
285 | GTask *task; |
286 | TestInteraction *self; |
287 | |
288 | g_assert (TEST_IS_INTERACTION (interaction)); |
289 | self = TEST_INTERACTION (interaction); |
290 | |
291 | g_assert (g_thread_self () == self->test->interaction_thread); |
292 | |
293 | g_assert (G_IS_TLS_CONNECTION (connection)); |
294 | g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
295 | g_assert (unused_flags == 0); |
296 | |
297 | task = g_task_new (source_object: self, cancellable, callback, callback_data: user_data); |
298 | |
299 | /* |
300 | * IRL would call g_tls_connection_set_certificate(). But here just touch |
301 | * the connection in a detectable way. |
302 | */ |
303 | g_object_set_data (G_OBJECT (connection), key: "chosen-certificate" , data: "my-certificate" ); |
304 | g_task_return_int (task, result: G_TLS_INTERACTION_HANDLED); |
305 | g_object_unref (object: task); |
306 | } |
307 | |
308 | static GTlsInteractionResult |
309 | test_interaction_request_certificate_finish_success (GTlsInteraction *interaction, |
310 | GAsyncResult *result, |
311 | GError **error) |
312 | { |
313 | TestInteraction *self; |
314 | |
315 | g_assert (TEST_IS_INTERACTION (interaction)); |
316 | self = TEST_INTERACTION (interaction); |
317 | |
318 | g_assert (g_thread_self () == self->test->interaction_thread); |
319 | |
320 | g_assert (g_task_is_valid (result, interaction)); |
321 | g_assert (error != NULL); |
322 | g_assert (*error == NULL); |
323 | |
324 | return g_task_propagate_int (G_TASK (result), error); |
325 | } |
326 | |
327 | static void |
328 | test_interaction_request_certificate_async_failure (GTlsInteraction *interaction, |
329 | GTlsConnection *connection, |
330 | gint unused_flags, |
331 | GCancellable *cancellable, |
332 | GAsyncReadyCallback callback, |
333 | gpointer user_data) |
334 | { |
335 | GTask *task; |
336 | TestInteraction *self; |
337 | |
338 | g_assert (TEST_IS_INTERACTION (interaction)); |
339 | self = TEST_INTERACTION (interaction); |
340 | |
341 | g_assert (g_thread_self () == self->test->interaction_thread); |
342 | |
343 | g_assert (G_IS_TLS_CONNECTION (connection)); |
344 | g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
345 | g_assert (unused_flags == 0); |
346 | |
347 | task = g_task_new (source_object: self, cancellable, callback, callback_data: user_data); |
348 | |
349 | g_task_return_new_error (task, G_FILE_ERROR, code: G_FILE_ERROR_NOENT, format: "Another message" ); |
350 | g_object_unref (object: task); |
351 | } |
352 | |
353 | static GTlsInteractionResult |
354 | test_interaction_request_certificate_finish_failure (GTlsInteraction *interaction, |
355 | GAsyncResult *result, |
356 | GError **error) |
357 | { |
358 | TestInteraction *self; |
359 | |
360 | g_assert (TEST_IS_INTERACTION (interaction)); |
361 | self = TEST_INTERACTION (interaction); |
362 | |
363 | g_assert (g_thread_self () == self->test->interaction_thread); |
364 | |
365 | g_assert (g_task_is_valid (result, interaction)); |
366 | g_assert (error != NULL); |
367 | g_assert (*error == NULL); |
368 | |
369 | if (g_task_propagate_int (G_TASK (result), error) != -1) |
370 | g_assert_not_reached (); |
371 | |
372 | return G_TLS_INTERACTION_FAILED; |
373 | } |
374 | |
375 | static GTlsInteractionResult |
376 | test_interaction_request_certificate_sync_success (GTlsInteraction *interaction, |
377 | GTlsConnection *connection, |
378 | gint unused_flags, |
379 | GCancellable *cancellable, |
380 | GError **error) |
381 | { |
382 | TestInteraction *self; |
383 | |
384 | g_assert (TEST_IS_INTERACTION (interaction)); |
385 | self = TEST_INTERACTION (interaction); |
386 | |
387 | g_assert (g_thread_self () == self->test->interaction_thread); |
388 | |
389 | g_assert (G_IS_TLS_CONNECTION (connection)); |
390 | g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
391 | g_assert (error != NULL); |
392 | g_assert (*error == NULL); |
393 | |
394 | /* |
395 | * IRL would call g_tls_connection_set_certificate(). But here just touch |
396 | * the connection in a detectable way. |
397 | */ |
398 | g_object_set_data (G_OBJECT (connection), key: "chosen-certificate" , data: "my-certificate" ); |
399 | return G_TLS_INTERACTION_HANDLED; |
400 | } |
401 | |
402 | static GTlsInteractionResult |
403 | test_interaction_request_certificate_sync_failure (GTlsInteraction *interaction, |
404 | GTlsConnection *connection, |
405 | gint unused_flags, |
406 | GCancellable *cancellable, |
407 | GError **error) |
408 | { |
409 | TestInteraction *self; |
410 | |
411 | g_assert (TEST_IS_INTERACTION (interaction)); |
412 | self = TEST_INTERACTION (interaction); |
413 | |
414 | g_assert (g_thread_self () == self->test->interaction_thread); |
415 | |
416 | g_assert (G_IS_TLS_CONNECTION (connection)); |
417 | g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
418 | g_assert (unused_flags == 0); |
419 | g_assert (error != NULL); |
420 | g_assert (*error == NULL); |
421 | |
422 | g_set_error (err: error, G_FILE_ERROR, code: G_FILE_ERROR_NOENT, format: "Another message" ); |
423 | return G_TLS_INTERACTION_FAILED; |
424 | } |
425 | |
426 | /* ---------------------------------------------------------------------------- |
427 | * ACTUAL TESTS |
428 | */ |
429 | |
430 | static void |
431 | on_ask_password_async_call (GObject *source, |
432 | GAsyncResult *result, |
433 | gpointer user_data) |
434 | { |
435 | Test *test = user_data; |
436 | GTlsInteractionResult res; |
437 | GError *error = NULL; |
438 | |
439 | g_assert (G_IS_TLS_INTERACTION (source)); |
440 | g_assert (G_TLS_INTERACTION (source) == test->interaction); |
441 | |
442 | /* Check that this callback is being run in the right place */ |
443 | g_assert (g_thread_self () == test->interaction_thread); |
444 | |
445 | res = g_tls_interaction_ask_password_finish (interaction: test->interaction, result, |
446 | error: &error); |
447 | |
448 | /* Check that the results match the fixture */ |
449 | g_assert_cmpuint (test->fixture->result, ==, res); |
450 | switch (test->fixture->result) |
451 | { |
452 | case G_TLS_INTERACTION_HANDLED: |
453 | g_assert_no_error (error); |
454 | g_assert_cmpstr ((const gchar *)g_tls_password_get_value (test->password, NULL), ==, "the password" ); |
455 | break; |
456 | case G_TLS_INTERACTION_FAILED: |
457 | g_assert_error (error, test->fixture->error_domain, test->fixture->error_code); |
458 | g_assert_cmpstr (error->message, ==, test->fixture->error_message); |
459 | g_clear_error (err: &error); |
460 | break; |
461 | case G_TLS_INTERACTION_UNHANDLED: |
462 | g_assert_no_error (error); |
463 | break; |
464 | default: |
465 | g_assert_not_reached (); |
466 | } |
467 | |
468 | /* Signal the end of the test */ |
469 | g_main_loop_quit (loop: test->loop); |
470 | } |
471 | |
472 | static void |
473 | test_ask_password_async (Test *test, |
474 | gconstpointer unused) |
475 | { |
476 | /* This test only works with a main loop */ |
477 | g_assert (test->loop); |
478 | |
479 | g_tls_interaction_ask_password_async (interaction: test->interaction, |
480 | password: test->password, NULL, |
481 | callback: on_ask_password_async_call, |
482 | user_data: test); |
483 | |
484 | /* teardown waits until g_main_loop_quit(). called from callback */ |
485 | } |
486 | |
487 | static void |
488 | test_invoke_ask_password (Test *test, |
489 | gconstpointer unused) |
490 | { |
491 | GTlsInteractionResult res; |
492 | GError *error = NULL; |
493 | |
494 | res = g_tls_interaction_invoke_ask_password (interaction: test->interaction, password: test->password, |
495 | NULL, error: &error); |
496 | |
497 | /* Check that the results match the fixture */ |
498 | g_assert_cmpuint (test->fixture->result, ==, res); |
499 | switch (test->fixture->result) |
500 | { |
501 | case G_TLS_INTERACTION_HANDLED: |
502 | g_assert_no_error (error); |
503 | g_assert_cmpstr ((const gchar *)g_tls_password_get_value (test->password, NULL), ==, "the password" ); |
504 | break; |
505 | case G_TLS_INTERACTION_FAILED: |
506 | g_assert_error (error, test->fixture->error_domain, test->fixture->error_code); |
507 | g_assert_cmpstr (error->message, ==, test->fixture->error_message); |
508 | g_clear_error (err: &error); |
509 | break; |
510 | case G_TLS_INTERACTION_UNHANDLED: |
511 | g_assert_no_error (error); |
512 | break; |
513 | default: |
514 | g_assert_not_reached (); |
515 | } |
516 | |
517 | /* This allows teardown to stop if running with loop */ |
518 | if (test->loop) |
519 | g_main_loop_quit (loop: test->loop); |
520 | } |
521 | |
522 | static void |
523 | test_ask_password (Test *test, |
524 | gconstpointer unused) |
525 | { |
526 | GTlsInteractionResult res; |
527 | GError *error = NULL; |
528 | |
529 | res = g_tls_interaction_ask_password (interaction: test->interaction, password: test->password, |
530 | NULL, error: &error); |
531 | |
532 | /* Check that the results match the fixture */ |
533 | g_assert_cmpuint (test->fixture->result, ==, res); |
534 | switch (test->fixture->result) |
535 | { |
536 | case G_TLS_INTERACTION_HANDLED: |
537 | g_assert_no_error (error); |
538 | g_assert_cmpstr ((const gchar *)g_tls_password_get_value (test->password, NULL), ==, "the password" ); |
539 | break; |
540 | case G_TLS_INTERACTION_FAILED: |
541 | g_assert_error (error, test->fixture->error_domain, test->fixture->error_code); |
542 | g_assert_cmpstr (error->message, ==, test->fixture->error_message); |
543 | g_clear_error (err: &error); |
544 | break; |
545 | case G_TLS_INTERACTION_UNHANDLED: |
546 | g_assert_no_error (error); |
547 | break; |
548 | default: |
549 | g_assert_not_reached (); |
550 | } |
551 | |
552 | /* This allows teardown to stop if running with loop */ |
553 | if (test->loop) |
554 | g_main_loop_quit (loop: test->loop); |
555 | } |
556 | |
557 | static void |
558 | on_request_certificate_async_call (GObject *source, |
559 | GAsyncResult *result, |
560 | gpointer user_data) |
561 | { |
562 | Test *test = user_data; |
563 | GTlsInteractionResult res; |
564 | GError *error = NULL; |
565 | |
566 | g_assert (G_IS_TLS_INTERACTION (source)); |
567 | g_assert (G_TLS_INTERACTION (source) == test->interaction); |
568 | |
569 | /* Check that this callback is being run in the right place */ |
570 | g_assert (g_thread_self () == test->interaction_thread); |
571 | |
572 | res = g_tls_interaction_request_certificate_finish (interaction: test->interaction, result, error: &error); |
573 | |
574 | /* Check that the results match the fixture */ |
575 | g_assert_cmpuint (test->fixture->result, ==, res); |
576 | switch (test->fixture->result) |
577 | { |
578 | case G_TLS_INTERACTION_HANDLED: |
579 | g_assert_no_error (error); |
580 | g_assert_cmpstr (g_object_get_data (G_OBJECT (test->connection), "chosen-certificate" ), ==, "my-certificate" ); |
581 | break; |
582 | case G_TLS_INTERACTION_FAILED: |
583 | g_assert_error (error, test->fixture->error_domain, test->fixture->error_code); |
584 | g_assert_cmpstr (error->message, ==, test->fixture->error_message); |
585 | g_clear_error (err: &error); |
586 | break; |
587 | case G_TLS_INTERACTION_UNHANDLED: |
588 | g_assert_no_error (error); |
589 | break; |
590 | default: |
591 | g_assert_not_reached (); |
592 | } |
593 | |
594 | /* Signal the end of the test */ |
595 | g_main_loop_quit (loop: test->loop); |
596 | } |
597 | |
598 | static void |
599 | test_request_certificate_async (Test *test, |
600 | gconstpointer unused) |
601 | { |
602 | /* This test only works with a main loop */ |
603 | g_assert (test->loop); |
604 | |
605 | g_tls_interaction_request_certificate_async (interaction: test->interaction, |
606 | connection: test->connection, flags: 0, NULL, |
607 | callback: on_request_certificate_async_call, |
608 | user_data: test); |
609 | |
610 | /* teardown waits until g_main_loop_quit(). called from callback */ |
611 | } |
612 | |
613 | static void |
614 | test_invoke_request_certificate (Test *test, |
615 | gconstpointer unused) |
616 | { |
617 | GTlsInteractionResult res; |
618 | GError *error = NULL; |
619 | |
620 | res = g_tls_interaction_invoke_request_certificate (interaction: test->interaction, |
621 | connection: test->connection, |
622 | flags: 0, NULL, error: &error); |
623 | |
624 | /* Check that the results match the fixture */ |
625 | g_assert_cmpuint (test->fixture->result, ==, res); |
626 | switch (test->fixture->result) |
627 | { |
628 | case G_TLS_INTERACTION_HANDLED: |
629 | g_assert_no_error (error); |
630 | g_assert_cmpstr (g_object_get_data (G_OBJECT (test->connection), "chosen-certificate" ), ==, "my-certificate" ); |
631 | break; |
632 | case G_TLS_INTERACTION_FAILED: |
633 | g_assert_error (error, test->fixture->error_domain, test->fixture->error_code); |
634 | g_assert_cmpstr (error->message, ==, test->fixture->error_message); |
635 | g_clear_error (err: &error); |
636 | break; |
637 | case G_TLS_INTERACTION_UNHANDLED: |
638 | g_assert_no_error (error); |
639 | break; |
640 | default: |
641 | g_assert_not_reached (); |
642 | } |
643 | |
644 | /* This allows teardown to stop if running with loop */ |
645 | if (test->loop) |
646 | g_main_loop_quit (loop: test->loop); |
647 | } |
648 | |
649 | static void |
650 | test_request_certificate (Test *test, |
651 | gconstpointer unused) |
652 | { |
653 | GTlsInteractionResult res; |
654 | GError *error = NULL; |
655 | |
656 | res = g_tls_interaction_request_certificate (interaction: test->interaction, connection: test->connection, |
657 | flags: 0, NULL, error: &error); |
658 | |
659 | /* Check that the results match the fixture */ |
660 | g_assert_cmpuint (test->fixture->result, ==, res); |
661 | switch (test->fixture->result) |
662 | { |
663 | case G_TLS_INTERACTION_HANDLED: |
664 | g_assert_no_error (error); |
665 | g_assert_cmpstr (g_object_get_data (G_OBJECT (test->connection), "chosen-certificate" ), ==, "my-certificate" ); |
666 | break; |
667 | case G_TLS_INTERACTION_FAILED: |
668 | g_assert_error (error, test->fixture->error_domain, test->fixture->error_code); |
669 | g_assert_cmpstr (error->message, ==, test->fixture->error_message); |
670 | g_clear_error (err: &error); |
671 | break; |
672 | case G_TLS_INTERACTION_UNHANDLED: |
673 | g_assert_no_error (error); |
674 | break; |
675 | default: |
676 | g_assert_not_reached (); |
677 | } |
678 | |
679 | /* This allows teardown to stop if running with loop */ |
680 | if (test->loop) |
681 | g_main_loop_quit (loop: test->loop); |
682 | } |
683 | |
684 | /* ---------------------------------------------------------------------------- |
685 | * TEST SETUP |
686 | */ |
687 | |
688 | static void |
689 | setup_without_loop (Test *test, |
690 | gconstpointer user_data) |
691 | { |
692 | const Fixture *fixture = user_data; |
693 | GTlsInteractionClass *klass; |
694 | GTlsBackend *backend; |
695 | GError *error = NULL; |
696 | |
697 | test->fixture = fixture; |
698 | |
699 | test->interaction = g_object_new (TEST_TYPE_INTERACTION, NULL); |
700 | g_assert (TEST_IS_INTERACTION (test->interaction)); |
701 | |
702 | TEST_INTERACTION (test->interaction)->test = test; |
703 | |
704 | klass = G_TLS_INTERACTION_GET_CLASS (test->interaction); |
705 | klass->ask_password = fixture->ask_password_func; |
706 | klass->ask_password_async = fixture->ask_password_async_func; |
707 | klass->ask_password_finish = fixture->ask_password_finish_func; |
708 | klass->request_certificate = fixture->request_certificate_func; |
709 | klass->request_certificate_async = fixture->request_certificate_async_func; |
710 | klass->request_certificate_finish = fixture->request_certificate_finish_func; |
711 | |
712 | backend = g_object_new (G_TYPE_TEST_TLS_BACKEND, NULL); |
713 | test->connection = g_object_new (object_type: g_tls_backend_get_server_connection_type (backend), NULL); |
714 | g_assert_no_error (error); |
715 | g_object_unref (object: backend); |
716 | |
717 | test->password = g_tls_password_new (flags: 0, description: "Description" ); |
718 | test->test_thread = g_thread_self (); |
719 | |
720 | /* |
721 | * If no loop is running then interaction should happen in the same |
722 | * thread that the tests are running in. |
723 | */ |
724 | test->interaction_thread = test->test_thread; |
725 | } |
726 | |
727 | static void |
728 | teardown_without_loop (Test *test, |
729 | gconstpointer unused) |
730 | { |
731 | g_object_unref (object: test->connection); |
732 | g_object_unref (object: test->password); |
733 | |
734 | g_assert_finalize_object (test->interaction); |
735 | } |
736 | |
737 | typedef struct { |
738 | GMutex loop_mutex; |
739 | GCond loop_started; |
740 | gboolean started; |
741 | Test *test; |
742 | } ThreadLoop; |
743 | |
744 | static gpointer |
745 | thread_loop (gpointer user_data) |
746 | { |
747 | GMainContext *context = g_main_context_default (); |
748 | ThreadLoop *closure = user_data; |
749 | Test *test = closure->test; |
750 | |
751 | g_mutex_lock (mutex: &closure->loop_mutex); |
752 | |
753 | g_assert (test->loop_thread == g_thread_self ()); |
754 | g_assert (test->loop == NULL); |
755 | test->loop = g_main_loop_new (context, TRUE); |
756 | |
757 | g_main_context_acquire (context); |
758 | closure->started = TRUE; |
759 | g_cond_signal (cond: &closure->loop_started); |
760 | g_mutex_unlock (mutex: &closure->loop_mutex); |
761 | |
762 | while (g_main_loop_is_running (loop: test->loop)) |
763 | g_main_context_iteration (context, TRUE); |
764 | |
765 | g_main_context_release (context); |
766 | return test; |
767 | } |
768 | |
769 | static void |
770 | setup_with_thread_loop (Test *test, |
771 | gconstpointer user_data) |
772 | { |
773 | ThreadLoop closure; |
774 | |
775 | setup_without_loop (test, user_data); |
776 | |
777 | g_mutex_init (mutex: &closure.loop_mutex); |
778 | g_cond_init (cond: &closure.loop_started); |
779 | closure.started = FALSE; |
780 | closure.test = test; |
781 | |
782 | g_mutex_lock (mutex: &closure.loop_mutex); |
783 | test->loop_thread = g_thread_new (name: "loop" , func: thread_loop, data: &closure); |
784 | while (!closure.started) |
785 | g_cond_wait (cond: &closure.loop_started, mutex: &closure.loop_mutex); |
786 | g_mutex_unlock (mutex: &closure.loop_mutex); |
787 | |
788 | /* |
789 | * When a loop is running then interaction should always occur in the main |
790 | * context of that loop. |
791 | */ |
792 | test->interaction_thread = test->loop_thread; |
793 | |
794 | g_mutex_clear (mutex: &closure.loop_mutex); |
795 | g_cond_clear (cond: &closure.loop_started); |
796 | } |
797 | |
798 | static void |
799 | teardown_with_thread_loop (Test *test, |
800 | gconstpointer unused) |
801 | { |
802 | gpointer check; |
803 | |
804 | g_assert (test->loop_thread); |
805 | check = g_thread_join (thread: test->loop_thread); |
806 | g_assert (check == test); |
807 | test->loop_thread = NULL; |
808 | |
809 | g_main_loop_unref (loop: test->loop); |
810 | |
811 | teardown_without_loop (test, unused); |
812 | } |
813 | |
814 | static void |
815 | setup_with_normal_loop (Test *test, |
816 | gconstpointer user_data) |
817 | { |
818 | GMainContext *context; |
819 | |
820 | setup_without_loop (test, user_data); |
821 | |
822 | context = g_main_context_default (); |
823 | if (!g_main_context_acquire (context)) |
824 | g_assert_not_reached (); |
825 | |
826 | test->loop = g_main_loop_new (context, TRUE); |
827 | g_assert (g_main_loop_is_running (test->loop)); |
828 | } |
829 | |
830 | static void |
831 | teardown_with_normal_loop (Test *test, |
832 | gconstpointer unused) |
833 | { |
834 | GMainContext *context; |
835 | |
836 | context = g_main_context_default (); |
837 | while (g_main_loop_is_running (loop: test->loop)) |
838 | g_main_context_iteration (context, TRUE); |
839 | |
840 | g_main_context_release (context); |
841 | |
842 | /* Run test until complete */ |
843 | g_main_loop_unref (loop: test->loop); |
844 | test->loop = NULL; |
845 | |
846 | teardown_without_loop (test, unused); |
847 | } |
848 | |
849 | typedef void (*TestFunc) (Test *test, gconstpointer data); |
850 | |
851 | static void |
852 | test_with_async_ask_password (const gchar *name, |
853 | TestFunc setup, |
854 | TestFunc func, |
855 | TestFunc teardown) |
856 | { |
857 | gchar *test_name; |
858 | Fixture *fixture; |
859 | |
860 | /* Async implementation that succeeds */ |
861 | fixture = g_new0 (Fixture, 1); |
862 | fixture->ask_password_async_func = test_interaction_ask_password_async_success; |
863 | fixture->ask_password_finish_func = test_interaction_ask_password_finish_success; |
864 | fixture->ask_password_func = NULL; |
865 | fixture->result = G_TLS_INTERACTION_HANDLED; |
866 | test_name = g_strdup_printf (format: "%s/async-implementation-success" , name); |
867 | g_test_add (test_name, Test, fixture, setup, func, teardown); |
868 | g_free (mem: test_name); |
869 | g_ptr_array_add (array: fixtures, data: fixture); |
870 | |
871 | /* Async implementation that fails */ |
872 | fixture = g_new0 (Fixture, 1); |
873 | fixture->ask_password_async_func = test_interaction_ask_password_async_failure; |
874 | fixture->ask_password_finish_func = test_interaction_ask_password_finish_failure; |
875 | fixture->ask_password_func = NULL; |
876 | fixture->result = G_TLS_INTERACTION_FAILED; |
877 | fixture->error_domain = G_FILE_ERROR; |
878 | fixture->error_code = G_FILE_ERROR_ACCES; |
879 | fixture->error_message = "The message" ; |
880 | test_name = g_strdup_printf (format: "%s/async-implementation-failure" , name); |
881 | g_test_add (test_name, Test, fixture, setup, func, teardown); |
882 | g_free (mem: test_name); |
883 | g_ptr_array_add (array: fixtures, data: fixture); |
884 | } |
885 | |
886 | static void |
887 | test_with_unhandled_ask_password (const gchar *name, |
888 | TestFunc setup, |
889 | TestFunc func, |
890 | TestFunc teardown) |
891 | { |
892 | gchar *test_name; |
893 | Fixture *fixture; |
894 | |
895 | /* Unhandled implementation */ |
896 | fixture = g_new0 (Fixture, 1); |
897 | fixture->ask_password_async_func = NULL; |
898 | fixture->ask_password_finish_func = NULL; |
899 | fixture->ask_password_func = NULL; |
900 | fixture->result = G_TLS_INTERACTION_UNHANDLED; |
901 | test_name = g_strdup_printf (format: "%s/unhandled-implementation" , name); |
902 | g_test_add (test_name, Test, fixture, setup, func, teardown); |
903 | g_free (mem: test_name); |
904 | g_ptr_array_add (array: fixtures, data: fixture); |
905 | } |
906 | |
907 | static void |
908 | test_with_sync_ask_password (const gchar *name, |
909 | TestFunc setup, |
910 | TestFunc func, |
911 | TestFunc teardown) |
912 | { |
913 | gchar *test_name; |
914 | Fixture *fixture; |
915 | |
916 | /* Sync implementation that succeeds */ |
917 | fixture = g_new0 (Fixture, 1); |
918 | fixture->ask_password_async_func = NULL; |
919 | fixture->ask_password_finish_func = NULL; |
920 | fixture->ask_password_func = test_interaction_ask_password_sync_success; |
921 | fixture->result = G_TLS_INTERACTION_HANDLED; |
922 | test_name = g_strdup_printf (format: "%s/sync-implementation-success" , name); |
923 | g_test_add (test_name, Test, fixture, setup, func, teardown); |
924 | g_free (mem: test_name); |
925 | g_ptr_array_add (array: fixtures, data: fixture); |
926 | |
927 | /* Async implementation that fails */ |
928 | fixture = g_new0 (Fixture, 1); |
929 | fixture->ask_password_async_func = NULL; |
930 | fixture->ask_password_finish_func = NULL; |
931 | fixture->ask_password_func = test_interaction_ask_password_sync_failure; |
932 | fixture->result = G_TLS_INTERACTION_FAILED; |
933 | fixture->error_domain = G_FILE_ERROR; |
934 | fixture->error_code = G_FILE_ERROR_ACCES; |
935 | fixture->error_message = "The message" ; |
936 | test_name = g_strdup_printf (format: "%s/sync-implementation-failure" , name); |
937 | g_test_add (test_name, Test, fixture, setup, func, teardown); |
938 | g_free (mem: test_name); |
939 | g_ptr_array_add (array: fixtures, data: fixture); |
940 | } |
941 | |
942 | static void |
943 | test_with_all_ask_password (const gchar *name, |
944 | TestFunc setup, |
945 | TestFunc func, |
946 | TestFunc teardown) |
947 | { |
948 | test_with_unhandled_ask_password (name, setup, func, teardown); |
949 | test_with_async_ask_password (name, setup, func, teardown); |
950 | test_with_sync_ask_password (name, setup, func, teardown); |
951 | } |
952 | |
953 | static void |
954 | test_with_async_request_certificate (const gchar *name, |
955 | TestFunc setup, |
956 | TestFunc func, |
957 | TestFunc teardown) |
958 | { |
959 | gchar *test_name; |
960 | Fixture *fixture; |
961 | |
962 | /* Async implementation that succeeds */ |
963 | fixture = g_new0 (Fixture, 1); |
964 | fixture->request_certificate_async_func = test_interaction_request_certificate_async_success; |
965 | fixture->request_certificate_finish_func = test_interaction_request_certificate_finish_success; |
966 | fixture->request_certificate_func = NULL; |
967 | fixture->result = G_TLS_INTERACTION_HANDLED; |
968 | test_name = g_strdup_printf (format: "%s/async-implementation-success" , name); |
969 | g_test_add (test_name, Test, fixture, setup, func, teardown); |
970 | g_free (mem: test_name); |
971 | g_ptr_array_add (array: fixtures, data: fixture); |
972 | |
973 | /* Async implementation that fails */ |
974 | fixture = g_new0 (Fixture, 1); |
975 | fixture->request_certificate_async_func = test_interaction_request_certificate_async_failure; |
976 | fixture->request_certificate_finish_func = test_interaction_request_certificate_finish_failure; |
977 | fixture->request_certificate_func = NULL; |
978 | fixture->result = G_TLS_INTERACTION_FAILED; |
979 | fixture->error_domain = G_FILE_ERROR; |
980 | fixture->error_code = G_FILE_ERROR_NOENT; |
981 | fixture->error_message = "Another message" ; |
982 | test_name = g_strdup_printf (format: "%s/async-implementation-failure" , name); |
983 | g_test_add (test_name, Test, fixture, setup, func, teardown); |
984 | g_free (mem: test_name); |
985 | g_ptr_array_add (array: fixtures, data: fixture); |
986 | } |
987 | |
988 | static void |
989 | test_with_unhandled_request_certificate (const gchar *name, |
990 | TestFunc setup, |
991 | TestFunc func, |
992 | TestFunc teardown) |
993 | { |
994 | gchar *test_name; |
995 | Fixture *fixture; |
996 | |
997 | /* Unhandled implementation */ |
998 | fixture = g_new0 (Fixture, 1); |
999 | fixture->request_certificate_async_func = NULL; |
1000 | fixture->request_certificate_finish_func = NULL; |
1001 | fixture->request_certificate_func = NULL; |
1002 | fixture->result = G_TLS_INTERACTION_UNHANDLED; |
1003 | test_name = g_strdup_printf (format: "%s/unhandled-implementation" , name); |
1004 | g_test_add (test_name, Test, fixture, setup, func, teardown); |
1005 | g_free (mem: test_name); |
1006 | g_ptr_array_add (array: fixtures, data: fixture); |
1007 | } |
1008 | |
1009 | static void |
1010 | test_with_sync_request_certificate (const gchar *name, |
1011 | TestFunc setup, |
1012 | TestFunc func, |
1013 | TestFunc teardown) |
1014 | { |
1015 | gchar *test_name; |
1016 | Fixture *fixture; |
1017 | |
1018 | /* Sync implementation that succeeds */ |
1019 | fixture = g_new0 (Fixture, 1); |
1020 | fixture->request_certificate_async_func = NULL; |
1021 | fixture->request_certificate_finish_func = NULL; |
1022 | fixture->request_certificate_func = test_interaction_request_certificate_sync_success; |
1023 | fixture->result = G_TLS_INTERACTION_HANDLED; |
1024 | test_name = g_strdup_printf (format: "%s/sync-implementation-success" , name); |
1025 | g_test_add (test_name, Test, fixture, setup, func, teardown); |
1026 | g_free (mem: test_name); |
1027 | g_ptr_array_add (array: fixtures, data: fixture); |
1028 | |
1029 | /* Async implementation that fails */ |
1030 | fixture = g_new0 (Fixture, 1); |
1031 | fixture->request_certificate_async_func = NULL; |
1032 | fixture->request_certificate_finish_func = NULL; |
1033 | fixture->request_certificate_func = test_interaction_request_certificate_sync_failure; |
1034 | fixture->result = G_TLS_INTERACTION_FAILED; |
1035 | fixture->error_domain = G_FILE_ERROR; |
1036 | fixture->error_code = G_FILE_ERROR_NOENT; |
1037 | fixture->error_message = "Another message" ; |
1038 | test_name = g_strdup_printf (format: "%s/sync-implementation-failure" , name); |
1039 | g_test_add (test_name, Test, fixture, setup, func, teardown); |
1040 | g_free (mem: test_name); |
1041 | g_ptr_array_add (array: fixtures, data: fixture); |
1042 | } |
1043 | |
1044 | static void |
1045 | test_with_all_request_certificate (const gchar *name, |
1046 | TestFunc setup, |
1047 | TestFunc func, |
1048 | TestFunc teardown) |
1049 | { |
1050 | test_with_unhandled_request_certificate (name, setup, func, teardown); |
1051 | test_with_async_request_certificate (name, setup, func, teardown); |
1052 | test_with_sync_request_certificate (name, setup, func, teardown); |
1053 | } |
1054 | int |
1055 | main (int argc, |
1056 | char *argv[]) |
1057 | { |
1058 | gint ret; |
1059 | |
1060 | g_test_init (argc: &argc, argv: &argv, NULL); |
1061 | |
1062 | fixtures = g_ptr_array_new_with_free_func (element_free_func: g_free); |
1063 | |
1064 | /* Tests for g_tls_interaction_invoke_ask_password */ |
1065 | test_with_all_ask_password (name: "/tls-interaction/ask-password/invoke-with-loop" , |
1066 | setup: setup_with_thread_loop, func: test_invoke_ask_password, teardown: teardown_with_thread_loop); |
1067 | test_with_all_ask_password (name: "/tls-interaction/ask-password/invoke-without-loop" , |
1068 | setup: setup_without_loop, func: test_invoke_ask_password, teardown: teardown_without_loop); |
1069 | test_with_all_ask_password (name: "/tls-interaction/ask-password/invoke-in-loop" , |
1070 | setup: setup_with_normal_loop, func: test_invoke_ask_password, teardown: teardown_with_normal_loop); |
1071 | |
1072 | /* Tests for g_tls_interaction_ask_password */ |
1073 | test_with_unhandled_ask_password (name: "/tls-interaction/ask-password/sync" , |
1074 | setup: setup_without_loop, func: test_ask_password, teardown: teardown_without_loop); |
1075 | test_with_sync_ask_password (name: "/tls-interaction/ask-password/sync" , |
1076 | setup: setup_without_loop, func: test_ask_password, teardown: teardown_without_loop); |
1077 | |
1078 | /* Tests for g_tls_interaction_ask_password_async */ |
1079 | test_with_unhandled_ask_password (name: "/tls-interaction/ask-password/async" , |
1080 | setup: setup_with_normal_loop, func: test_ask_password_async, teardown: teardown_with_normal_loop); |
1081 | test_with_async_ask_password (name: "/tls-interaction/ask-password/async" , |
1082 | setup: setup_with_normal_loop, func: test_ask_password_async, teardown: teardown_with_normal_loop); |
1083 | |
1084 | /* Tests for g_tls_interaction_invoke_request_certificate */ |
1085 | test_with_all_request_certificate (name: "/tls-interaction/request-certificate/invoke-with-loop" , |
1086 | setup: setup_with_thread_loop, func: test_invoke_request_certificate, teardown: teardown_with_thread_loop); |
1087 | test_with_all_request_certificate (name: "/tls-interaction/request-certificate/invoke-without-loop" , |
1088 | setup: setup_without_loop, func: test_invoke_request_certificate, teardown: teardown_without_loop); |
1089 | test_with_all_request_certificate (name: "/tls-interaction/request-certificate/invoke-in-loop" , |
1090 | setup: setup_with_normal_loop, func: test_invoke_request_certificate, teardown: teardown_with_normal_loop); |
1091 | |
1092 | /* Tests for g_tls_interaction_ask_password */ |
1093 | test_with_unhandled_request_certificate (name: "/tls-interaction/request-certificate/sync" , |
1094 | setup: setup_without_loop, func: test_request_certificate, teardown: teardown_without_loop); |
1095 | test_with_sync_request_certificate (name: "/tls-interaction/request-certificate/sync" , |
1096 | setup: setup_without_loop, func: test_request_certificate, teardown: teardown_without_loop); |
1097 | |
1098 | /* Tests for g_tls_interaction_ask_password_async */ |
1099 | test_with_unhandled_request_certificate (name: "/tls-interaction/request-certificate/async" , |
1100 | setup: setup_with_normal_loop, func: test_request_certificate_async, teardown: teardown_with_normal_loop); |
1101 | test_with_async_request_certificate (name: "/tls-interaction/request-certificate/async" , |
1102 | setup: setup_with_normal_loop, func: test_request_certificate_async, teardown: teardown_with_normal_loop); |
1103 | |
1104 | ret = g_test_run(); |
1105 | g_ptr_array_free (array: fixtures, TRUE); |
1106 | return ret; |
1107 | } |
1108 | |