Branch data Line data Source code
1 : : /*
2 : : * gnome-keyring
3 : : *
4 : : * Copyright (C) 2011 Collabora Ltd.
5 : : *
6 : : * This program is free software; you can redistribute it and/or modify
7 : : * it under the terms of the GNU Lesser General Public License as
8 : : * published by the Free Software Foundation; either version 2.1 of
9 : : * the License, or (at your option) any later version.
10 : : *
11 : : * This program is distributed in the hope that it will be useful, but
12 : : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Lesser General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General Public
17 : : * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : : *
19 : : * Author: Stef Walter <stfew@collabora.co.uk>
20 : : */
21 : :
22 : : #include "config.h"
23 : :
24 : : #include "gcr-mock-prompter.h"
25 : : #include "gcr-prompt.h"
26 : :
27 : : #include "egg/egg-error.h"
28 : :
29 : : #include <gobject/gvaluecollector.h>
30 : :
31 : : #include <string.h>
32 : :
33 : : /**
34 : : * GcrMockPrompter:
35 : : *
36 : : * A mock [class@SystemPrompter] used for testing against.
37 : : *
38 : : * Use [func@MockPrompter.start] to start the mock prompter in another
39 : : * thread. The returned string is the dbus address of the mock prompter.
40 : : * You can pass this to [func@SystemPrompt.open] as the prompter bus name.
41 : : *
42 : : * Use the [func@MockPrompter.expect_confirm_ok] function and friends before
43 : : * prompting to verify that the prompts are displayed as expected, and to
44 : : * provide a response.
45 : : */
46 : :
47 : : #define GCR_TYPE_MOCK_PROMPT (_gcr_mock_prompt_get_type ())
48 : 435 : G_DECLARE_FINAL_TYPE (GcrMockPrompt, _gcr_mock_prompt,
49 : : GCR, MOCK_PROMPT,
50 : : GObject)
51 : :
52 : : enum {
53 : : PROP_0,
54 : :
55 : : PROP_TITLE,
56 : : PROP_MESSAGE,
57 : : PROP_DESCRIPTION,
58 : : PROP_WARNING,
59 : : PROP_PASSWORD_NEW,
60 : : PROP_PASSWORD_STRENGTH,
61 : : PROP_CHOICE_LABEL,
62 : : PROP_CHOICE_CHOSEN,
63 : : PROP_CALLER_WINDOW,
64 : : PROP_CONTINUE_LABEL,
65 : : PROP_CANCEL_LABEL,
66 : : };
67 : :
68 : : typedef struct _MockProperty {
69 : : const char *name;
70 : : GValue value;
71 : : } MockProperty;
72 : :
73 : : struct _GcrMockPrompt {
74 : : GObject parent;
75 : : GHashTable *properties;
76 : : gboolean disposed;
77 : : };
78 : :
79 : : typedef struct {
80 : : gboolean close;
81 : : gboolean proceed;
82 : : gchar *password;
83 : : GList *properties;
84 : : } MockResponse;
85 : :
86 : : typedef struct {
87 : : /* Owned by the calling thread */
88 : : GMutex *mutex;
89 : : GCond *start_cond;
90 : : GThread *thread;
91 : :
92 : : guint delay_msec;
93 : : GQueue responses;
94 : :
95 : : /* Owned by the prompter thread*/
96 : : GcrSystemPrompter *prompter;
97 : : GDBusConnection *connection;
98 : : GMainLoop *loop;
99 : : } ThreadData;
100 : :
101 : : static gint prompts_a_prompting = 0;
102 : : static ThreadData *running = NULL;
103 : :
104 : : static void gcr_mock_prompt_iface (GcrPromptInterface *iface);
105 : :
106 [ + + + - : 466 : G_DEFINE_TYPE_WITH_CODE (GcrMockPrompt, _gcr_mock_prompt, G_TYPE_OBJECT,
+ + ]
107 : : G_IMPLEMENT_INTERFACE (GCR_TYPE_PROMPT, gcr_mock_prompt_iface);
108 : : );
109 : :
110 : : static void
111 : 452 : mock_property_free (gpointer data)
112 : : {
113 : 452 : MockProperty *param = data;
114 : 452 : g_value_unset (¶m->value);
115 : 452 : g_free (param);
116 : 452 : }
117 : :
118 : : static void
119 : 14 : mock_response_free (gpointer data)
120 : : {
121 : 14 : MockResponse *response = data;
122 : :
123 [ - + ]: 14 : if (response == NULL)
124 : 0 : return;
125 : :
126 : 14 : g_free (response->password);
127 : 14 : g_list_free_full (response->properties, mock_property_free);
128 : 14 : g_free (response);
129 : : }
130 : :
131 : : static void
132 : 144 : blank_string_property (GHashTable *properties,
133 : : const gchar *property)
134 : : {
135 : : MockProperty *param;
136 : :
137 : 144 : param = g_new0 (MockProperty, 1);
138 : 144 : param->name = property;
139 : 144 : g_value_init (¶m->value, G_TYPE_STRING);
140 : 144 : g_value_set_string (¶m->value, "");
141 : 144 : g_hash_table_insert (properties, (gpointer)param->name, param);
142 : 144 : }
143 : :
144 : :
145 : : static void
146 : 36 : blank_boolean_property (GHashTable *properties,
147 : : const gchar *property)
148 : : {
149 : : MockProperty *param;
150 : :
151 : 36 : param = g_new0 (MockProperty, 1);
152 : 36 : param->name = property;
153 : 36 : g_value_init (¶m->value, G_TYPE_BOOLEAN);
154 : 36 : g_value_set_boolean (¶m->value, FALSE);
155 : 36 : g_hash_table_insert (properties, (gpointer)param->name, param);
156 : 36 : }
157 : :
158 : : static void
159 : 18 : blank_int_property (GHashTable *properties,
160 : : const gchar *property)
161 : : {
162 : : MockProperty *param;
163 : :
164 : 18 : param = g_new0 (MockProperty, 1);
165 : 18 : param->name = property;
166 : 18 : g_value_init (¶m->value, G_TYPE_INT);
167 : 18 : g_value_set_int (¶m->value, 0);
168 : 18 : g_hash_table_insert (properties, (gpointer)param->name, param);
169 : 18 : }
170 : :
171 : : static void
172 : 18 : _gcr_mock_prompt_init (GcrMockPrompt *self)
173 : : {
174 : 18 : g_atomic_int_add (&prompts_a_prompting, 1);
175 : :
176 : 18 : self->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
177 : : NULL, mock_property_free);
178 : :
179 : 18 : blank_string_property (self->properties, "title");
180 : 18 : blank_string_property (self->properties, "message");
181 : 18 : blank_string_property (self->properties, "description");
182 : 18 : blank_string_property (self->properties, "warning");
183 : 18 : blank_string_property (self->properties, "choice-label");
184 : 18 : blank_string_property (self->properties, "caller-window");
185 : 18 : blank_string_property (self->properties, "continue-label");
186 : 18 : blank_string_property (self->properties, "cancel-label");
187 : :
188 : 18 : blank_boolean_property (self->properties, "choice-chosen");
189 : 18 : blank_boolean_property (self->properties, "password-new");
190 : :
191 : 18 : blank_int_property (self->properties, "password-strength");
192 : 18 : }
193 : :
194 : : static void
195 : 259 : _gcr_mock_prompt_set_property (GObject *obj,
196 : : guint prop_id,
197 : : const GValue *value,
198 : : GParamSpec *pspec)
199 : : {
200 : 259 : GcrMockPrompt *self = GCR_MOCK_PROMPT (obj);
201 : : MockProperty *param;
202 : :
203 [ + - ]: 259 : switch (prop_id) {
204 : 259 : case PROP_TITLE:
205 : : case PROP_MESSAGE:
206 : : case PROP_DESCRIPTION:
207 : : case PROP_WARNING:
208 : : case PROP_PASSWORD_NEW:
209 : : case PROP_CHOICE_LABEL:
210 : : case PROP_CHOICE_CHOSEN:
211 : : case PROP_CALLER_WINDOW:
212 : : case PROP_CONTINUE_LABEL:
213 : : case PROP_CANCEL_LABEL:
214 : 259 : param = g_new0 (MockProperty, 1);
215 : 259 : param->name = pspec->name;
216 : 259 : g_value_init (¶m->value, pspec->value_type);
217 : 259 : g_value_copy (value, ¶m->value);
218 : 259 : g_hash_table_replace (self->properties, (gpointer)param->name, param);
219 : 259 : g_object_notify (G_OBJECT (self), param->name);
220 : 259 : break;
221 : 0 : default:
222 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
223 : 0 : break;
224 : : }
225 : 259 : }
226 : :
227 : : static void
228 : 112 : _gcr_mock_prompt_get_property (GObject *obj,
229 : : guint prop_id,
230 : : GValue *value,
231 : : GParamSpec *pspec)
232 : : {
233 : 112 : GcrMockPrompt *self = GCR_MOCK_PROMPT (obj);
234 : : MockProperty *param;
235 : :
236 [ + - ]: 112 : switch (prop_id) {
237 : 112 : case PROP_TITLE:
238 : : case PROP_MESSAGE:
239 : : case PROP_DESCRIPTION:
240 : : case PROP_WARNING:
241 : : case PROP_PASSWORD_NEW:
242 : : case PROP_PASSWORD_STRENGTH:
243 : : case PROP_CHOICE_LABEL:
244 : : case PROP_CHOICE_CHOSEN:
245 : : case PROP_CALLER_WINDOW:
246 : : case PROP_CONTINUE_LABEL:
247 : : case PROP_CANCEL_LABEL:
248 : 112 : param = g_hash_table_lookup (self->properties, pspec->name);
249 [ - + ]: 112 : g_return_if_fail (param != NULL);
250 : 112 : g_value_copy (¶m->value, value);
251 : 112 : break;
252 : 0 : default:
253 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
254 : 0 : break;
255 : : }
256 : : }
257 : :
258 : :
259 : : static void
260 : 34 : _gcr_mock_prompt_dispose (GObject *obj)
261 : : {
262 : 34 : GcrMockPrompt *self = GCR_MOCK_PROMPT (obj);
263 : :
264 [ + + ]: 34 : if (!self->disposed) {
265 : 18 : g_atomic_int_add (&prompts_a_prompting, -1);
266 : 18 : self->disposed = TRUE;
267 : : }
268 : :
269 : 34 : G_OBJECT_CLASS (_gcr_mock_prompt_parent_class)->dispose (obj);
270 : 34 : }
271 : :
272 : : static void
273 : 16 : _gcr_mock_prompt_finalize (GObject *obj)
274 : : {
275 : 16 : GcrMockPrompt *self = GCR_MOCK_PROMPT (obj);
276 : :
277 : 16 : g_hash_table_destroy(self->properties);
278 : :
279 : 16 : G_OBJECT_CLASS (_gcr_mock_prompt_parent_class)->finalize (obj);
280 : 16 : }
281 : :
282 : : static gboolean
283 : 10 : value_equal (const GValue *a, const GValue *b)
284 : : {
285 : 10 : gboolean ret = FALSE;
286 : :
287 [ - + ]: 10 : g_assert (G_VALUE_TYPE (a) == G_VALUE_TYPE (b));
288 : :
289 [ + - + - : 10 : switch (G_VALUE_TYPE (a)) {
- - - +
- ]
290 : 1 : case G_TYPE_BOOLEAN:
291 : 1 : ret = (g_value_get_boolean (a) == g_value_get_boolean (b));
292 : 1 : break;
293 : 0 : case G_TYPE_UCHAR:
294 : 0 : ret = (g_value_get_uchar (a) == g_value_get_uchar (b));
295 : 0 : break;
296 : 1 : case G_TYPE_INT:
297 : 1 : ret = (g_value_get_int (a) == g_value_get_int (b));
298 : 1 : break;
299 : 0 : case G_TYPE_UINT:
300 : 0 : ret = (g_value_get_uint (a) == g_value_get_uint (b));
301 : 0 : break;
302 : 0 : case G_TYPE_INT64:
303 : 0 : ret = (g_value_get_int64 (a) == g_value_get_int64 (b));
304 : 0 : break;
305 : 0 : case G_TYPE_UINT64:
306 : 0 : ret = (g_value_get_uint64 (a) == g_value_get_uint64 (b));
307 : 0 : break;
308 : 0 : case G_TYPE_DOUBLE:
309 : 0 : ret = (g_value_get_double (a) == g_value_get_double (b));
310 : 0 : break;
311 : 8 : case G_TYPE_STRING:
312 : 8 : ret = (g_strcmp0 (g_value_get_string (a), g_value_get_string (b)) == 0);
313 : 8 : break;
314 : 0 : default:
315 : 0 : g_critical ("no support for comparing of type %s", g_type_name (G_VALUE_TYPE (a)));
316 : 0 : break;
317 : : }
318 : :
319 : 10 : return ret;
320 : : }
321 : :
322 : : static void
323 : 13 : prompt_set_or_check_properties (GcrMockPrompt *self,
324 : : GList *properties)
325 : : {
326 : 13 : GValue value = G_VALUE_INIT;
327 : : GObjectClass *object_class;
328 : : MockProperty *param;
329 : : GParamSpec *spec;
330 : : GList *l;
331 : :
332 : 13 : object_class = G_OBJECT_GET_CLASS (self);
333 [ + - + + ]: 24 : for (l = properties; l != NULL; l = g_list_next (l)) {
334 : 11 : param = l->data;
335 : :
336 : 11 : spec = g_object_class_find_property (object_class, param->name);
337 [ - + ]: 11 : g_assert (spec != NULL);
338 : :
339 : : /* For these we set the value */
340 [ + + ]: 11 : if (g_str_equal (param->name, "choice-chosen")) {
341 : 1 : g_object_set_property (G_OBJECT (self), param->name, ¶m->value);
342 : :
343 : : /* For others we check that the value is correct */
344 : : } else {
345 : 10 : g_value_init (&value, G_VALUE_TYPE (¶m->value));
346 : 10 : g_object_get_property (G_OBJECT (self), param->name, &value);
347 [ - + ]: 10 : if (!value_equal (&value, ¶m->value)) {
348 : 0 : gchar *expected = g_strdup_value_contents (¶m->value);
349 : 0 : gchar *actual = g_strdup_value_contents (&value);
350 : 0 : g_critical ("expected prompt property '%s' to be %s, but it "
351 : : "is instead %s", param->name, expected, actual);
352 : 0 : g_free (expected);
353 : 0 : g_free (actual);
354 : : }
355 : 10 : g_value_unset (&value);
356 : : }
357 : : }
358 : 13 : }
359 : :
360 : : static void
361 : 1 : _gcr_mock_prompt_class_init (GcrMockPromptClass *klass)
362 : : {
363 : 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
364 : :
365 : 1 : gobject_class->get_property = _gcr_mock_prompt_get_property;
366 : 1 : gobject_class->set_property = _gcr_mock_prompt_set_property;
367 : 1 : gobject_class->dispose = _gcr_mock_prompt_dispose;
368 : 1 : gobject_class->finalize = _gcr_mock_prompt_finalize;
369 : :
370 : 1 : g_object_class_override_property (gobject_class, PROP_TITLE, "title");
371 : 1 : g_object_class_override_property (gobject_class, PROP_MESSAGE, "message");
372 : 1 : g_object_class_override_property (gobject_class, PROP_DESCRIPTION, "description");
373 : 1 : g_object_class_override_property (gobject_class, PROP_WARNING, "warning");
374 : 1 : g_object_class_override_property (gobject_class, PROP_CALLER_WINDOW, "caller-window");
375 : 1 : g_object_class_override_property (gobject_class, PROP_CHOICE_LABEL, "choice-label");
376 : 1 : g_object_class_override_property (gobject_class, PROP_CHOICE_CHOSEN, "choice-chosen");
377 : 1 : g_object_class_override_property (gobject_class, PROP_PASSWORD_NEW, "password-new");
378 : 1 : g_object_class_override_property (gobject_class, PROP_PASSWORD_STRENGTH, "password-strength");
379 : 1 : g_object_class_override_property (gobject_class, PROP_CONTINUE_LABEL, "continue-label");
380 : 1 : g_object_class_override_property (gobject_class, PROP_CANCEL_LABEL, "cancel-label");
381 : 1 : }
382 : :
383 : : static gboolean
384 : 11 : on_timeout_complete (gpointer data)
385 : : {
386 : 11 : GSimpleAsyncResult *res = data;
387 : 11 : g_simple_async_result_complete (res);
388 : 11 : return FALSE;
389 : : }
390 : :
391 : : static gboolean
392 : 1 : on_timeout_complete_and_close (gpointer data)
393 : : {
394 : 1 : GSimpleAsyncResult *res = data;
395 : 1 : GcrPrompt *prompt = GCR_PROMPT (g_async_result_get_source_object (data));
396 : 1 : g_simple_async_result_complete (res);
397 : 1 : gcr_prompt_close (prompt);
398 : 1 : g_object_unref (prompt);
399 : 1 : return FALSE;
400 : : }
401 : :
402 : : static void
403 : 12 : destroy_unref_source (gpointer source)
404 : : {
405 [ - + ]: 12 : if (!g_source_is_destroyed (source))
406 : 0 : g_source_destroy (source);
407 : 12 : g_source_unref (source);
408 : 12 : }
409 : :
410 : : static void
411 : 7 : gcr_mock_prompt_confirm_async (GcrPrompt *prompt,
412 : : GCancellable *cancellable,
413 : : GAsyncReadyCallback callback,
414 : : gpointer user_data)
415 : : {
416 : 7 : GcrMockPrompt *self = GCR_MOCK_PROMPT (prompt);
417 : 7 : GSourceFunc complete_func = on_timeout_complete;
418 : : GSimpleAsyncResult *res;
419 : : MockResponse *response;
420 : : GSource *source;
421 : : guint delay_msec;
422 : :
423 : 7 : g_mutex_lock (running->mutex);
424 : 7 : delay_msec = running->delay_msec;
425 : 7 : response = g_queue_pop_head (&running->responses);
426 : 7 : g_mutex_unlock (running->mutex);
427 : :
428 : 7 : res = g_simple_async_result_new (G_OBJECT (prompt), callback, user_data,
429 : : gcr_mock_prompt_confirm_async);
430 : :
431 [ - + ]: 7 : if (response == NULL) {
432 : 0 : g_critical ("password prompt requested, but not expected");
433 : 0 : g_simple_async_result_set_op_res_gboolean (res, FALSE);
434 : :
435 [ + + ]: 7 : } else if (response->close) {
436 : 1 : complete_func = on_timeout_complete_and_close;
437 : 1 : g_simple_async_result_set_op_res_gboolean (res, FALSE);
438 : :
439 [ - + ]: 6 : } else if (response->password) {
440 : 0 : g_critical ("confirmation prompt requested, but password prompt expected");
441 : 0 : g_simple_async_result_set_op_res_gboolean (res, FALSE);
442 : :
443 : : } else {
444 : 6 : prompt_set_or_check_properties (self, response->properties);
445 : 6 : g_simple_async_result_set_op_res_gboolean (res, response->proceed);
446 : : }
447 : :
448 [ - + ]: 7 : if (delay_msec > 0)
449 : 0 : source = g_timeout_source_new (delay_msec);
450 : : else
451 : 7 : source = g_idle_source_new ();
452 : :
453 : 7 : g_source_set_callback (source, complete_func, g_object_ref (res), g_object_unref);
454 : 7 : g_source_attach (source, g_main_context_get_thread_default ());
455 : 7 : g_object_set_data_full (G_OBJECT (self), "delay-source", source, destroy_unref_source);
456 : :
457 : 7 : mock_response_free (response);
458 : 7 : g_object_unref (res);
459 : 7 : }
460 : :
461 : : static GcrPromptReply
462 : 7 : gcr_mock_prompt_confirm_finish (GcrPrompt *prompt,
463 : : GAsyncResult *result,
464 : : GError **error)
465 : : {
466 [ - + ]: 7 : g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (prompt),
467 : : gcr_mock_prompt_confirm_async), GCR_PROMPT_REPLY_CANCEL);
468 : :
469 : 7 : return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result)) ?
470 : 7 : GCR_PROMPT_REPLY_CONTINUE : GCR_PROMPT_REPLY_CANCEL;
471 : : }
472 : :
473 : : static void
474 : 6 : ensure_password_strength (GcrMockPrompt *self,
475 : : const gchar *password)
476 : : {
477 : : MockProperty *param;
478 : : gint strength;
479 : :
480 : 6 : strength = strlen (password) > 0 ? 1 : 0;
481 : 6 : param = g_new0 (MockProperty, 1);
482 : 6 : param->name = "password-strength";
483 : 6 : g_value_init (¶m->value, G_TYPE_INT);
484 : 6 : g_value_set_int (¶m->value, strength);
485 : 6 : g_hash_table_replace (self->properties, (gpointer)param->name, param);
486 : 6 : g_object_notify (G_OBJECT (self), param->name);
487 : 6 : }
488 : :
489 : : static void
490 : 7 : gcr_mock_prompt_password_async (GcrPrompt *prompt,
491 : : GCancellable *cancellable,
492 : : GAsyncReadyCallback callback,
493 : : gpointer user_data)
494 : : {
495 : 7 : GcrMockPrompt *self = GCR_MOCK_PROMPT (prompt);
496 : 7 : GSourceFunc complete_func = on_timeout_complete;
497 : : GSimpleAsyncResult *res;
498 : : MockResponse *response;
499 : : GSource *source;
500 : : guint delay_msec;
501 : :
502 : 7 : g_mutex_lock (running->mutex);
503 : 7 : delay_msec = running->delay_msec;
504 : 7 : response = g_queue_pop_head (&running->responses);
505 : 7 : g_mutex_unlock (running->mutex);
506 : :
507 : 7 : res = g_simple_async_result_new (G_OBJECT (prompt), callback, user_data,
508 : : gcr_mock_prompt_password_async);
509 : :
510 [ - + ]: 7 : if (response == NULL) {
511 : 0 : g_critical ("password prompt requested, but not expected");
512 : 0 : g_simple_async_result_set_op_res_gpointer (res, NULL, NULL);
513 : :
514 [ - + ]: 7 : } else if (response->close) {
515 : 0 : g_simple_async_result_set_op_res_gpointer (res, NULL, NULL);
516 : 0 : complete_func = on_timeout_complete_and_close;
517 : :
518 [ - + ]: 7 : } else if (!response->password) {
519 : 0 : g_critical ("password prompt requested, but confirmation prompt expected");
520 : 0 : g_simple_async_result_set_op_res_gpointer (res, NULL, NULL);
521 : :
522 [ + + ]: 7 : } else if (!response->proceed) {
523 : 1 : prompt_set_or_check_properties (self, response->properties);
524 : 1 : g_simple_async_result_set_op_res_gpointer (res, NULL, NULL);
525 : :
526 : : } else {
527 : 6 : ensure_password_strength (self, response->password);
528 : 6 : prompt_set_or_check_properties (self, response->properties);
529 : 6 : g_simple_async_result_set_op_res_gpointer (res, response->password, g_free);
530 : 6 : response->password = NULL;
531 : : }
532 : :
533 : 7 : mock_response_free (response);
534 : :
535 [ + + ]: 7 : if (delay_msec > 0)
536 : 2 : source = g_timeout_source_new (delay_msec);
537 : : else
538 : 5 : source = g_idle_source_new ();
539 : :
540 : 7 : g_source_set_callback (source, complete_func, g_object_ref (res), g_object_unref);
541 : 7 : g_source_attach (source, g_main_context_get_thread_default ());
542 : 7 : g_object_set_data_full (G_OBJECT (self), "delay-source", source, destroy_unref_source);
543 : :
544 : 7 : g_object_unref (res);
545 : 7 : }
546 : :
547 : :
548 : : static const gchar *
549 : 5 : gcr_mock_prompt_password_finish (GcrPrompt *prompt,
550 : : GAsyncResult *result,
551 : : GError **error)
552 : : {
553 [ - + ]: 5 : g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (prompt),
554 : : gcr_mock_prompt_password_async), NULL);
555 : :
556 : 5 : return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
557 : :
558 : : }
559 : :
560 : : static void
561 : 1 : gcr_mock_prompt_iface (GcrPromptInterface *iface)
562 : : {
563 : 1 : iface->prompt_confirm_async = gcr_mock_prompt_confirm_async;
564 : 1 : iface->prompt_confirm_finish = gcr_mock_prompt_confirm_finish;
565 : 1 : iface->prompt_password_async = gcr_mock_prompt_password_async;
566 : 1 : iface->prompt_password_finish = gcr_mock_prompt_password_finish;
567 : 1 : }
568 : :
569 : : static GList *
570 : 11 : build_properties (GObjectClass *object_class,
571 : : const gchar *first_property,
572 : : va_list var_args)
573 : : {
574 : 11 : GList *result = NULL;
575 : : const gchar *name;
576 : :
577 : 11 : name = first_property;
578 [ + + ]: 22 : while (name) {
579 : 11 : GValue value = G_VALUE_INIT;
580 : : MockProperty *parameter;
581 : : GParamSpec *spec;
582 : 11 : gchar *error = NULL;
583 : :
584 : 11 : spec = g_object_class_find_property (object_class, name);
585 [ - + ]: 11 : if (spec == NULL) {
586 : 0 : g_warning ("prompt object class has no property named '%s'", name);
587 : 0 : break;
588 : : }
589 : :
590 [ - + - - ]: 11 : if ((spec->flags & G_PARAM_CONSTRUCT_ONLY) && !(spec->flags & G_PARAM_READABLE)) {
591 : 0 : g_warning ("prompt property '%s' can't be set after construction", name);
592 : 0 : break;
593 : : }
594 : :
595 [ + - - - : 22 : G_VALUE_COLLECT_INIT (&value, spec->value_type, var_args, 0, &error);
+ - + + ]
596 [ - + ]: 11 : if (error != NULL) {
597 : 0 : g_warning ("%s", error);
598 : 0 : g_free (error);
599 : 0 : g_value_unset (&value);
600 : 0 : break;
601 : : }
602 : :
603 : 11 : parameter = g_new0 (MockProperty, 1);
604 : 11 : parameter->name = g_intern_string (name);
605 : 11 : memcpy (¶meter->value, &value, sizeof (value));
606 : 11 : result = g_list_prepend (result, parameter);
607 : :
608 : 11 : name = va_arg (var_args, gchar *);
609 : : }
610 : :
611 : 11 : return result;
612 : : }
613 : :
614 : : /**
615 : : * gcr_mock_prompter_is_prompting:
616 : : *
617 : : * Check if the mock prompter is showing any prompts.
618 : : *
619 : : * Returns: whether prompting
620 : : */
621 : : gboolean
622 : 0 : gcr_mock_prompter_is_prompting (void)
623 : : {
624 : 0 : return g_atomic_int_get (&prompts_a_prompting) > 0;
625 : : }
626 : :
627 : : /**
628 : : * gcr_mock_prompter_get_delay_msec:
629 : : *
630 : : * Get the delay in milliseconds before the mock prompter completes
631 : : * an expected prompt.
632 : : *
633 : : * Returns: the delay
634 : : */
635 : : guint
636 : 0 : gcr_mock_prompter_get_delay_msec (void)
637 : : {
638 : : guint delay_msec;
639 : :
640 [ # # ]: 0 : g_assert (running != NULL);
641 : 0 : g_mutex_lock (running->mutex);
642 : 0 : delay_msec = running->delay_msec;
643 : 0 : g_mutex_unlock (running->mutex);
644 : :
645 : 0 : return delay_msec;
646 : : }
647 : :
648 : : /**
649 : : * gcr_mock_prompter_set_delay_msec:
650 : : * @delay_msec: prompt response delay in milliseconds
651 : : *
652 : : * Set the delay in milliseconds before the mock prompter completes
653 : : * an expected prompt.
654 : : */
655 : : void
656 : 2 : gcr_mock_prompter_set_delay_msec (guint delay_msec)
657 : : {
658 [ - + ]: 2 : g_assert (running != NULL);
659 : 2 : g_mutex_lock (running->mutex);
660 : 2 : running->delay_msec = delay_msec;
661 : 2 : g_mutex_unlock (running->mutex);
662 : 2 : }
663 : :
664 : : /**
665 : : * gcr_mock_prompter_expect_confirm_ok:
666 : : * @first_property_name: the first property name in the argument list or %NULL
667 : : * @...: properties to expect
668 : : *
669 : : * Queue an expected response on the mock prompter.
670 : : *
671 : : * Expects a confirmation prompt, and then confirms that prompt by
672 : : * simulating a click on the ok button.
673 : : *
674 : : * Additional property pairs for the prompt can be added in the argument
675 : : * list, in the same way that you would with g_object_new().
676 : : *
677 : : * If the "choice-chosen" property is specified then that value will be
678 : : * set on the prompt as if the user had changed the value.
679 : : *
680 : : * All other properties will be checked against the prompt, and an error
681 : : * will occur if they do not match the value set on the prompt.
682 : : */
683 : : void
684 : 5 : gcr_mock_prompter_expect_confirm_ok (const gchar *first_property_name,
685 : : ...)
686 : : {
687 : : MockResponse *response;
688 : : gpointer klass;
689 : : va_list var_args;
690 : :
691 [ - + ]: 5 : g_assert (running != NULL);
692 : :
693 : 5 : g_mutex_lock (running->mutex);
694 : :
695 : 5 : response = g_new0 (MockResponse, 1);
696 : 5 : response->password = NULL;
697 : 5 : response->proceed = TRUE;
698 : :
699 : 5 : klass = g_type_class_ref (GCR_TYPE_MOCK_PROMPT);
700 : :
701 : 5 : va_start (var_args, first_property_name);
702 : 5 : response->properties = build_properties (G_OBJECT_CLASS (klass), first_property_name, var_args);
703 : 5 : va_end (var_args);
704 : :
705 : 5 : g_type_class_unref (klass);
706 : 5 : g_queue_push_tail (&running->responses, response);
707 : 5 : g_mutex_unlock (running->mutex);
708 : 5 : }
709 : :
710 : : /**
711 : : * gcr_mock_prompter_expect_confirm_cancel:
712 : : *
713 : : * Queue an expected response on the mock prompter.
714 : : *
715 : : * Expects a confirmation prompt, and then cancels that prompt.
716 : : */
717 : : void
718 : 1 : gcr_mock_prompter_expect_confirm_cancel (void)
719 : : {
720 : : MockResponse *response;
721 : :
722 [ - + ]: 1 : g_assert (running != NULL);
723 : :
724 : 1 : g_mutex_lock (running->mutex);
725 : :
726 : 1 : response = g_new0 (MockResponse, 1);
727 : 1 : response->password = NULL;
728 : 1 : response->proceed = FALSE;
729 : :
730 : 1 : g_queue_push_tail (&running->responses, response);
731 : :
732 : 1 : g_mutex_unlock (running->mutex);
733 : 1 : }
734 : :
735 : : /**
736 : : * gcr_mock_prompter_expect_password_ok:
737 : : * @password: the password to return from the prompt
738 : : * @first_property_name: the first property name in the argument list or %NULL
739 : : * @...: properties to expect
740 : : *
741 : : * Queue an expected response on the mock prompter.
742 : : *
743 : : * Expects a password prompt, and returns @password as if the user had entered
744 : : * it and clicked the ok button.
745 : : *
746 : : * Additional property pairs for the prompt can be added in the argument
747 : : * list, in the same way that you would with g_object_new().
748 : : *
749 : : * If the "choice-chosen" property is specified then that value will be
750 : : * set on the prompt as if the user had changed the value.
751 : : *
752 : : * All other properties will be checked against the prompt, and an error
753 : : * will occur if they do not match the value set on the prompt.
754 : : */
755 : : void
756 : 6 : gcr_mock_prompter_expect_password_ok (const gchar *password,
757 : : const gchar *first_property_name,
758 : : ...)
759 : : {
760 : : MockResponse *response;
761 : : gpointer klass;
762 : : va_list var_args;
763 : :
764 [ - + ]: 6 : g_assert (running != NULL);
765 [ - + ]: 6 : g_assert (password != NULL);
766 : :
767 : 6 : g_mutex_lock (running->mutex);
768 : :
769 : 6 : response = g_new0 (MockResponse, 1);
770 : 6 : response->password = g_strdup (password);
771 : 6 : response->proceed = TRUE;
772 : :
773 : 6 : klass = g_type_class_ref (GCR_TYPE_MOCK_PROMPT);
774 : :
775 : 6 : va_start (var_args, first_property_name);
776 : 6 : response->properties = build_properties (G_OBJECT_CLASS (klass), first_property_name, var_args);
777 : 6 : va_end (var_args);
778 : :
779 : 6 : g_type_class_unref (klass);
780 : 6 : g_queue_push_tail (&running->responses, response);
781 : :
782 : 6 : g_mutex_unlock (running->mutex);
783 : 6 : }
784 : :
785 : : /**
786 : : * gcr_mock_prompter_expect_password_cancel:
787 : : *
788 : : * Queue an expected response on the mock prompter.
789 : : *
790 : : * Expects a password prompt, and then cancels that prompt.
791 : : */
792 : : void
793 : 1 : gcr_mock_prompter_expect_password_cancel (void)
794 : : {
795 : : MockResponse *response;
796 : :
797 [ - + ]: 1 : g_assert (running != NULL);
798 : :
799 : 1 : g_mutex_lock (running->mutex);
800 : :
801 : 1 : response = g_new0 (MockResponse, 1);
802 : 1 : response->password = g_strdup ("");
803 : 1 : response->proceed = FALSE;
804 : :
805 : 1 : g_queue_push_tail (&running->responses, response);
806 : :
807 : 1 : g_mutex_unlock (running->mutex);
808 : 1 : }
809 : :
810 : : /**
811 : : * gcr_mock_prompter_expect_close:
812 : : *
813 : : * Queue an expected response on the mock prompter.
814 : : *
815 : : * Expects any prompt, and closes the prompt when it gets it.
816 : : */
817 : : void
818 : 1 : gcr_mock_prompter_expect_close (void)
819 : : {
820 : : MockResponse *response;
821 : :
822 [ - + ]: 1 : g_assert (running != NULL);
823 : :
824 : 1 : g_mutex_lock (running->mutex);
825 : :
826 : 1 : response = g_new0 (MockResponse, 1);
827 : 1 : response->close = TRUE;
828 : :
829 : 1 : g_queue_push_tail (&running->responses, response);
830 : :
831 : 1 : g_mutex_unlock (running->mutex);
832 : 1 : }
833 : :
834 : : /**
835 : : * gcr_mock_prompter_is_expecting:
836 : : *
837 : : * Check if the mock prompter is expecting a response. This will be %TRUE
838 : : * when one of the <literal>gcr_mock_prompter_expect_xxx<!-- -->()</literal>
839 : : * functions have been used to queue an expected prompt, but that prompt
840 : : * response has not be 'used' yet.
841 : : *
842 : : * Returns: whether expecting a prompt
843 : : */
844 : : gboolean
845 : 0 : gcr_mock_prompter_is_expecting (void)
846 : : {
847 : : gboolean expecting;
848 : :
849 [ # # ]: 0 : g_assert (running != NULL);
850 : :
851 : 0 : g_mutex_lock (running->mutex);
852 : :
853 : 0 : expecting = !g_queue_is_empty (&running->responses);
854 : :
855 : 0 : g_mutex_unlock (running->mutex);
856 : :
857 : 0 : return expecting;
858 : : }
859 : :
860 : : static gboolean
861 : 18 : on_idle_signal_cond (gpointer user_data)
862 : : {
863 : 18 : GCond *cond = user_data;
864 : 18 : g_cond_signal (cond);
865 : 18 : return FALSE; /* Don't run again */
866 : : }
867 : :
868 : : /*
869 : : * These next few functions test the new-prompt signals of
870 : : * GcrSystemPrompter. They should probably be in tests, but
871 : : * don't fit there nicely.
872 : : */
873 : : static GcrPrompt *
874 : 8 : on_new_prompt_skipped (GcrSystemPrompter *prompter,
875 : : gpointer user_data)
876 : : {
877 [ - + + - : 8 : g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (prompter), NULL);
+ - - + ]
878 : 8 : return NULL;
879 : : }
880 : :
881 : : static GcrPrompt *
882 : 8 : on_new_prompt_creates (GcrSystemPrompter *prompter,
883 : : gpointer user_data)
884 : : {
885 [ - + + - : 8 : g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (prompter), NULL);
+ - - + ]
886 : 8 : return g_object_new (GCR_TYPE_MOCK_PROMPT, NULL);
887 : : }
888 : :
889 : : static GcrPrompt *
890 : 0 : on_new_prompt_not_called (GcrSystemPrompter *prompter,
891 : : gpointer user_data)
892 : : {
893 [ # # # # : 0 : g_return_val_if_fail (GCR_IS_SYSTEM_PROMPTER (prompter), NULL);
# # # # ]
894 : 0 : g_return_val_if_reached (NULL);
895 : : }
896 : :
897 : : static gpointer
898 : 18 : mock_prompter_thread (gpointer data)
899 : : {
900 : 18 : ThreadData *thread_data = data;
901 : 18 : GDBusConnection *connection = NULL;
902 : : GMainContext *context;
903 : 18 : GError *error = NULL;
904 : : GSource *idle;
905 : : gchar *address;
906 : :
907 : 18 : g_mutex_lock (thread_data->mutex);
908 : 18 : context = g_main_context_new ();
909 : 18 : g_main_context_push_thread_default (context);
910 : :
911 : : /*
912 : : * Random choice between signals, and prompt-gtype style of creating
913 : : * GcrPrompt objects.
914 : : */
915 : :
916 [ + + ]: 18 : if (g_random_boolean ()) {
917 : : /* Allows GcrSystemPrompter to create the prompts directly */
918 : 10 : thread_data->prompter = gcr_system_prompter_new (GCR_SYSTEM_PROMPTER_SINGLE,
919 : : GCR_TYPE_MOCK_PROMPT);
920 : :
921 : : } else {
922 : : /* Create the prompt objects in signal handler */
923 : 8 : thread_data->prompter = gcr_system_prompter_new (GCR_SYSTEM_PROMPTER_SINGLE, 0);
924 : 8 : g_signal_connect (thread_data->prompter, "new-prompt", G_CALLBACK (on_new_prompt_skipped), NULL);
925 : 8 : g_signal_connect (thread_data->prompter, "new-prompt", G_CALLBACK (on_new_prompt_creates), NULL);
926 : 8 : g_signal_connect (thread_data->prompter, "new-prompt", G_CALLBACK (on_new_prompt_not_called), NULL);
927 : : }
928 : :
929 : 18 : address = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, NULL, &error);
930 [ + - ]: 18 : if (error == NULL) {
931 : 18 : connection = g_dbus_connection_new_for_address_sync (address,
932 : : G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
933 : : G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
934 : : NULL, NULL, &error);
935 [ + - ]: 18 : if (error == NULL) {
936 : 18 : thread_data->connection = connection;
937 : 18 : gcr_system_prompter_register (GCR_SYSTEM_PROMPTER (thread_data->prompter),
938 : : connection);
939 : : } else {
940 : 0 : g_critical ("couldn't create connection: %s", error->message);
941 : 0 : g_error_free (error);
942 : : }
943 : :
944 : 18 : g_free (address);
945 : : }
946 : :
947 [ - + ]: 18 : if (error != NULL) {
948 : 0 : g_critical ("mock prompter couldn't get session bus address: %s",
949 : : egg_error_message (error));
950 : 0 : g_clear_error (&error);
951 : : }
952 : :
953 : 18 : thread_data->loop = g_main_loop_new (context, FALSE);
954 : 18 : g_mutex_unlock (thread_data->mutex);
955 : :
956 : 18 : idle = g_idle_source_new ();
957 : 18 : g_source_set_callback (idle, on_idle_signal_cond, thread_data->start_cond, NULL);
958 : 18 : g_source_attach (idle, context);
959 : 18 : g_source_unref (idle);
960 : :
961 : 18 : g_main_loop_run (thread_data->loop);
962 : :
963 : 18 : g_mutex_lock (thread_data->mutex);
964 : 18 : g_main_context_pop_thread_default (context);
965 : :
966 : 18 : gcr_system_prompter_unregister (thread_data->prompter, TRUE);
967 : 18 : g_object_unref (thread_data->prompter);
968 : 18 : thread_data->prompter = NULL;
969 : :
970 [ + - ]: 18 : if (connection) {
971 : 18 : thread_data->connection = NULL;
972 : :
973 [ + + ]: 18 : if (!g_dbus_connection_is_closed (connection)) {
974 [ - + ]: 17 : if (!g_dbus_connection_flush_sync (connection, NULL, &error)) {
975 : 0 : g_critical ("connection flush failed: %s", error->message);
976 : 0 : g_error_free (error);
977 : : }
978 [ - + ]: 17 : if (!g_dbus_connection_close_sync (connection, NULL, &error)) {
979 : 0 : g_critical ("connection close failed: %s", error->message);
980 : 0 : g_error_free (error);
981 : : }
982 : : }
983 : :
984 : 18 : g_object_unref (connection);
985 : : }
986 : :
987 [ + + ]: 40 : while (g_main_context_iteration (context, FALSE));
988 : :
989 : 18 : g_main_context_unref (context);
990 : 18 : g_main_loop_unref (thread_data->loop);
991 : 18 : thread_data->loop = NULL;
992 : :
993 : 18 : g_mutex_unlock (thread_data->mutex);
994 : 18 : return thread_data;
995 : : }
996 : :
997 : : /**
998 : : * gcr_mock_prompter_start:
999 : : *
1000 : : * Start the mock prompter. This is often used from the
1001 : : * <literal>setup<!-- -->()</literal> function of tests.
1002 : : *
1003 : : * Starts the mock prompter in an additional thread. Use the returned DBus bus
1004 : : * name with gcr_system_prompt_open_for_prompter() to connect to this prompter.
1005 : : *
1006 : : * Returns: the bus name that the mock prompter is listening on
1007 : : */
1008 : : const gchar *
1009 : 18 : gcr_mock_prompter_start (void)
1010 : : {
1011 : 18 : GError *error = NULL;
1012 : :
1013 [ - + ]: 18 : g_assert (running == NULL);
1014 : :
1015 : 18 : running = g_new0 (ThreadData, 1);
1016 : 18 : running->mutex = g_new0 (GMutex, 1);
1017 : 18 : g_mutex_init (running->mutex);
1018 : 18 : running->start_cond = g_new0 (GCond, 1);
1019 : 18 : g_cond_init (running->start_cond);
1020 : 18 : g_queue_init (&running->responses);
1021 : 18 : g_mutex_lock (running->mutex);
1022 : :
1023 : 18 : running->thread = g_thread_new ("mock-prompter", mock_prompter_thread, running);
1024 [ - + ]: 18 : if (error != NULL)
1025 : 0 : g_error ("mock prompter couldn't start thread: %s", error->message);
1026 : :
1027 : 18 : g_cond_wait (running->start_cond, running->mutex);
1028 [ - + ]: 18 : g_assert (running->loop);
1029 [ - + ]: 18 : g_assert (running->prompter);
1030 : 18 : g_mutex_unlock (running->mutex);
1031 : :
1032 : 18 : return g_dbus_connection_get_unique_name (running->connection);
1033 : : }
1034 : :
1035 : : /**
1036 : : * gcr_mock_prompter_disconnect:
1037 : : *
1038 : : * Disconnect the mock prompter
1039 : : */
1040 : : void
1041 : 1 : gcr_mock_prompter_disconnect (void)
1042 : : {
1043 : 1 : GError *error = NULL;
1044 : :
1045 [ - + ]: 1 : g_assert (running != NULL);
1046 [ - + ]: 1 : g_assert (running->connection);
1047 : :
1048 : 1 : g_dbus_connection_close_sync (running->connection, NULL, &error);
1049 [ - + ]: 1 : if (error != NULL) {
1050 : 0 : g_critical ("disconnect connection close failed: %s", error->message);
1051 : 0 : g_error_free (error);
1052 : : }
1053 : 1 : }
1054 : :
1055 : : /**
1056 : : * gcr_mock_prompter_stop:
1057 : : *
1058 : : * Stop the mock prompter. This is often used from the
1059 : : * <literal>teardown<!-- -->()</literal> function of tests.
1060 : : */
1061 : : void
1062 : 18 : gcr_mock_prompter_stop (void)
1063 : : {
1064 : : ThreadData *check;
1065 : :
1066 [ - + ]: 18 : g_assert (running != NULL);
1067 : :
1068 : 18 : g_mutex_lock (running->mutex);
1069 [ - + ]: 18 : g_assert (running->loop != NULL);
1070 : 18 : g_main_loop_quit (running->loop);
1071 : 18 : g_mutex_unlock (running->mutex);
1072 : :
1073 : 18 : check = g_thread_join (running->thread);
1074 [ - + ]: 18 : g_assert (check == running);
1075 : :
1076 : 18 : g_queue_foreach (&running->responses, (GFunc)mock_response_free, NULL);
1077 : 18 : g_queue_clear (&running->responses);
1078 : :
1079 : 18 : g_cond_clear (running->start_cond);
1080 : 18 : g_free (running->start_cond);
1081 : 18 : g_mutex_clear (running->mutex);
1082 : 18 : g_free (running->mutex);
1083 : :
1084 : 18 : g_free (running);
1085 : 18 : running = NULL;
1086 : 18 : }
|