]> Pileus Git - ~andy/gtk/blob - gtk/tests/filechooser.c
Change FSF Address
[~andy/gtk] / gtk / tests / filechooser.c
1 /* GTK - The GIMP Toolkit
2  * autotestfilechooser.c: Automated unit tests for the GtkFileChooser widget
3  * Copyright (C) 2005, Novell, Inc.
4  *
5  * Authors:
6  *   Federico Mena-Quintero <federico@novell.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /* TODO:
23  *
24  * - In test_reload_sequence(), test that the selection is preserved properly
25  *   between unmap/map.
26  *
27  * - More tests!
28  */
29
30 #define SLEEP_DURATION  100
31
32 #include "config.h"
33 #include <string.h>
34 #include <glib/gprintf.h>
35 #include <gtk/gtk.h>
36 #include "gtk/gtkfilechooserprivate.h"
37 #include "gtk/gtkfilechooserdefault.h"
38 #include "gtk/gtkfilechooserentry.h"
39
40 static void
41 log_test (gboolean passed, const char *test_name, ...)
42 {
43   va_list args;
44   char *str;
45
46   va_start (args, test_name);
47   str = g_strdup_vprintf (test_name, args);
48   va_end (args);
49
50   if (g_test_verbose())
51     g_printf ("%s: %s\n", passed ? "PASSED" : "FAILED", str);
52   g_free (str);
53 }
54
55 typedef void (* SetFilenameFn) (GtkFileChooser *chooser, gpointer data);
56 typedef gboolean (* CompareFilenameFn) (GtkFileChooser *chooser, gpointer data);
57
58 struct test_set_filename_closure {
59   GtkWidget *chooser;
60   GtkWidget *accept_button;
61   gboolean focus_button;
62 };
63
64 static gboolean
65 set_filename_timeout_cb (gpointer data)
66 {
67   struct test_set_filename_closure *closure;
68
69   closure = data;
70
71   if (closure->focus_button)
72     gtk_widget_grab_focus (closure->accept_button);
73
74   gtk_button_clicked (GTK_BUTTON (closure->accept_button));
75
76   return FALSE;
77 }
78
79
80 static guint wait_for_idle_id = 0;
81
82 static gboolean
83 wait_for_idle_idle (gpointer data)
84 {
85   wait_for_idle_id = 0;
86
87   return G_SOURCE_REMOVE;
88 }
89
90 static void
91 wait_for_idle (void)
92 {
93   wait_for_idle_id = g_idle_add_full (G_PRIORITY_LOW + 100,
94                                       wait_for_idle_idle,
95                                       NULL, NULL);
96
97   while (wait_for_idle_id)
98     gtk_main_iteration ();
99 }
100
101 static gboolean
102 test_set_filename (GtkFileChooserAction action,
103                    gboolean focus_button,
104                    SetFilenameFn set_filename_fn,const
105                    CompareFilenameFn compare_filename_fn,
106                    gpointer data)
107 {
108   GtkWidget *chooser;
109   struct test_set_filename_closure closure;
110   gboolean retval;
111
112   chooser = gtk_file_chooser_dialog_new ("hello", NULL, action,
113                                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
114                                          NULL);
115
116   closure.chooser = chooser;
117   closure.accept_button = gtk_dialog_add_button (GTK_DIALOG (chooser), GTK_STOCK_OK, GTK_RESPONSE_ACCEPT);
118   closure.focus_button = focus_button;
119
120   gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_ACCEPT);
121
122   (* set_filename_fn) (GTK_FILE_CHOOSER (chooser), data);
123
124   gdk_threads_add_timeout_full (G_MAXINT, SLEEP_DURATION, set_filename_timeout_cb, &closure, NULL);
125   gtk_dialog_run (GTK_DIALOG (chooser));
126
127   retval = (* compare_filename_fn) (GTK_FILE_CHOOSER (chooser), data);
128
129   gtk_widget_destroy (chooser);
130
131   return retval;
132 }
133
134 static void
135 set_filename_cb (GtkFileChooser *chooser, gpointer data)
136 {
137   const char *filename;
138
139   filename = data;
140   gtk_file_chooser_set_filename (chooser, filename);
141 }
142
143 static gboolean
144 compare_filename_cb (GtkFileChooser *chooser, gpointer data)
145 {
146   const char *filename;
147   char *out_filename;
148   gboolean retval;
149
150   filename = data;
151   out_filename = gtk_file_chooser_get_filename (chooser);
152
153   if (out_filename)
154     {
155       retval = (strcmp (out_filename, filename) == 0);
156       g_free (out_filename);
157     } else
158       retval = FALSE;
159
160   return retval;
161 }
162
163 static gboolean
164 test_black_box_set_filename (GtkFileChooserAction action, const char *filename, gboolean focus_button)
165 {
166   gboolean passed;
167
168   passed = test_set_filename (action, focus_button, set_filename_cb, compare_filename_cb, (char *) filename);
169
170   log_test (passed, "set_filename: action %d, focus_button=%s",
171             (int) action,
172             focus_button ? "TRUE" : "FALSE");
173
174   return passed;
175
176 }
177
178 struct current_name_closure {
179         const char *path;
180         const char *current_name;
181 };
182
183 static void
184 set_current_name_cb (GtkFileChooser *chooser, gpointer data)
185 {
186   struct current_name_closure *closure;
187
188   closure = data;
189
190   gtk_file_chooser_set_current_folder (chooser, closure->path);
191   gtk_file_chooser_set_current_name (chooser, closure->current_name);
192 }
193
194 static gboolean
195 compare_current_name_cb (GtkFileChooser *chooser, gpointer data)
196 {
197   struct current_name_closure *closure;
198   char *out_filename;
199   gboolean retval;
200
201   closure = data;
202
203   out_filename = gtk_file_chooser_get_filename (chooser);
204
205   if (out_filename)
206     {
207       char *filename;
208
209       filename = g_build_filename (closure->path, closure->current_name, NULL);
210       retval = (strcmp (filename, out_filename) == 0);
211       g_free (filename);
212       g_free (out_filename);
213     } else
214       retval = FALSE;
215
216   return retval;
217 }
218
219 static gboolean
220 test_black_box_set_current_name (GtkFileChooserAction action, const char *path, const char *current_name, gboolean focus_button)
221 {
222   struct current_name_closure closure;
223   gboolean passed;
224
225   closure.path = path;
226   closure.current_name = current_name;
227
228   passed = test_set_filename (action, focus_button,
229                               set_current_name_cb, compare_current_name_cb, &closure);
230
231   log_test (passed, "set_current_name, focus_button=%s", focus_button ? "TRUE" : "FALSE");
232
233   return passed;
234 }
235
236 /* FIXME: fails in CREATE_FOLDER mode when FOLDER_NAME == "/" */
237
238 #if 0
239 #define FILE_NAME "/nonexistent"
240 #define FOLDER_NAME "/etc"
241 #else
242 #define FILE_NAME "/etc/passwd"
243 #define FOLDER_NAME "/etc"
244 #endif
245
246 #define CURRENT_NAME "parangaricutirimicuaro.txt"
247 #define CURRENT_NAME_FOLDER "parangaricutirimicuaro"
248
249 /* https://bugzilla.novell.com/show_bug.cgi?id=184875
250  * http://bugzilla.gnome.org/show_bug.cgi?id=347066
251  * http://bugzilla.gnome.org/show_bug.cgi?id=346058
252  */
253 static void
254 test_black_box (void)
255 {
256   gboolean passed;
257   char *cwd;
258
259   passed = TRUE;
260
261   passed = passed && test_black_box_set_filename (GTK_FILE_CHOOSER_ACTION_OPEN, FILE_NAME, FALSE);
262   g_assert (passed);
263   passed = passed && test_black_box_set_filename (GTK_FILE_CHOOSER_ACTION_OPEN, FILE_NAME, TRUE);
264   g_assert (passed);
265   passed = passed && test_black_box_set_filename (GTK_FILE_CHOOSER_ACTION_SAVE, FILE_NAME, FALSE);
266   g_assert (passed);
267   passed = passed && test_black_box_set_filename (GTK_FILE_CHOOSER_ACTION_SAVE, FILE_NAME, TRUE);
268   g_assert (passed);
269   passed = passed && test_black_box_set_filename (GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, FOLDER_NAME, FALSE);
270   g_assert (passed);
271   passed = passed && test_black_box_set_filename (GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, FOLDER_NAME, TRUE);
272   g_assert (passed);
273   passed = passed && test_black_box_set_filename (GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER, FOLDER_NAME, FALSE);
274   g_assert (passed);
275   passed = passed && test_black_box_set_filename (GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER, FOLDER_NAME, TRUE);
276   g_assert (passed);
277
278   cwd = g_get_current_dir ();
279
280   passed = passed && test_black_box_set_current_name (GTK_FILE_CHOOSER_ACTION_SAVE, cwd, CURRENT_NAME, FALSE);
281   g_assert (passed);
282   passed = passed && test_black_box_set_current_name (GTK_FILE_CHOOSER_ACTION_SAVE, cwd, CURRENT_NAME, TRUE);
283   g_assert (passed);
284   passed = passed && test_black_box_set_current_name (GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER, cwd, CURRENT_NAME_FOLDER, FALSE);
285   g_assert (passed);
286   passed = passed && test_black_box_set_current_name (GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER, cwd, CURRENT_NAME_FOLDER, TRUE);
287   g_assert (passed);
288
289   g_free (cwd);
290
291   log_test (passed, "Black box tests");
292 }
293
294 struct confirm_overwrite_closure {
295   GtkWidget *chooser;
296   GtkWidget *accept_button;
297   gint confirm_overwrite_signal_emitted;
298   gchar *extension;
299 };
300
301 static GtkFileChooserConfirmation
302 confirm_overwrite_cb (GtkFileChooser *chooser, gpointer data)
303 {
304   struct confirm_overwrite_closure *closure = data;
305
306   if (g_test_verbose())
307     printf ("bling!\n");
308   closure->confirm_overwrite_signal_emitted += 1;
309
310   return GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME;
311 }
312
313 static void
314 overwrite_response_cb (GtkFileChooser *chooser, gint response, gpointer data)
315 {
316   struct confirm_overwrite_closure *closure = data;
317   char *filename;
318
319   if (g_test_verbose())
320     printf ("plong!\n");
321
322   if (response != GTK_RESPONSE_ACCEPT)
323     return;
324
325   filename = gtk_file_chooser_get_filename (chooser);
326
327   if (!g_str_has_suffix (filename, closure->extension))
328     {
329       char *basename;
330
331       basename = g_path_get_basename (filename);
332       g_free (filename);
333
334       filename = g_strconcat (basename, closure->extension, NULL);
335       gtk_file_chooser_set_current_name (chooser, filename);
336
337       g_signal_stop_emission_by_name (chooser, "response");
338       gtk_dialog_response (GTK_DIALOG (chooser), GTK_RESPONSE_ACCEPT);
339     }
340 }
341
342 static gboolean
343 confirm_overwrite_timeout_cb (gpointer data)
344 {
345   struct confirm_overwrite_closure *closure;
346
347   closure = data;
348   gtk_button_clicked (GTK_BUTTON (closure->accept_button));
349
350   return FALSE;
351 }
352
353 /* http://bugzilla.gnome.org/show_bug.cgi?id=347883 */
354 static gboolean
355 test_confirm_overwrite_for_path (const char *path, gboolean append_extension)
356 {
357   gboolean passed;
358   struct confirm_overwrite_closure closure;
359   char *filename;
360
361   passed = TRUE;
362
363   closure.extension = NULL;
364   closure.confirm_overwrite_signal_emitted = 0;
365   closure.chooser = gtk_file_chooser_dialog_new ("hello", NULL, GTK_FILE_CHOOSER_ACTION_SAVE,
366                                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
367                                                  NULL);
368   closure.accept_button = gtk_dialog_add_button (GTK_DIALOG (closure.chooser),
369                                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT);
370   gtk_dialog_set_default_response (GTK_DIALOG (closure.chooser), GTK_RESPONSE_ACCEPT);
371
372   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (closure.chooser), TRUE);
373
374   g_signal_connect (closure.chooser, "confirm-overwrite",
375                     G_CALLBACK (confirm_overwrite_cb), &closure);
376
377   if (append_extension)
378     {
379       char *extension;
380
381       filename = g_path_get_dirname (path);
382       gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (closure.chooser), filename);
383       g_free (filename);
384
385       filename = g_path_get_basename (path);
386       extension = strchr (filename, '.');
387
388       if (extension)
389         {
390           closure.extension = g_strdup (extension);
391           *extension = '\0';
392         }
393
394       gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (closure.chooser), filename);
395       g_free (filename);
396
397       g_signal_connect (closure.chooser, "response",
398                         G_CALLBACK (overwrite_response_cb), &closure);
399     }
400   else
401     {
402       gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (closure.chooser), path);
403     }
404
405   gdk_threads_add_timeout_full (G_MAXINT, SLEEP_DURATION, confirm_overwrite_timeout_cb, &closure, NULL);
406   gtk_dialog_run (GTK_DIALOG (closure.chooser));
407
408   filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (closure.chooser));
409   passed = passed && filename && (strcmp (filename, path) == 0);
410   g_free (filename);
411   
412   gtk_widget_destroy (closure.chooser);
413
414   passed = passed && (1 == closure.confirm_overwrite_signal_emitted);
415
416   log_test (passed, "Confirm overwrite for %s", path);
417
418   return passed;
419 }
420
421 static void
422 test_confirm_overwrite (void)
423 {
424   gboolean passed = TRUE;
425
426   /* first test for a file we know will always exist */
427   passed = passed && test_confirm_overwrite_for_path ("/etc/passwd", FALSE); 
428   g_assert (passed);
429   passed = passed && test_confirm_overwrite_for_path ("/etc/resolv.conf", TRUE); 
430   g_assert (passed);
431 }
432
433 static const GtkFileChooserAction open_actions[] = {
434   GTK_FILE_CHOOSER_ACTION_OPEN,
435   GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
436 };
437
438 static const GtkFileChooserAction save_actions[] = {
439   GTK_FILE_CHOOSER_ACTION_SAVE,
440   GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
441 };
442
443
444 static gboolean
445 has_action (const GtkFileChooserAction *actions,
446             int n_actions,
447             GtkFileChooserAction sought_action)
448 {
449   int i;
450
451   for (i = 0; i < n_actions; i++)
452     if (actions[i] == sought_action)
453       return TRUE;
454
455   return FALSE;
456 }
457
458 static const char *
459 get_action_name (GtkFileChooserAction action)
460 {
461   GEnumClass *enum_class;
462   GEnumValue *enum_value;
463
464   enum_class = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
465   if (!enum_class)
466     g_error ("BUG: get_action_name(): no GEnumClass for GTK_TYPE_FILE_CHOOSER_ACTION");
467
468   enum_value = g_enum_get_value (enum_class, (int) action);
469   if (!enum_value)
470     g_error ("BUG: get_action_name(): no GEnumValue for GtkFileChooserAction %d", (int) action);
471
472   return enum_value->value_name;
473 }
474
475 static GtkFileChooserDefault *
476 get_impl_from_dialog (GtkWidget *dialog)
477 {
478   GtkFileChooserDialog *d;
479   GtkFileChooserDialogPrivate *dialog_priv;
480   GtkFileChooserWidget *chooser_widget;
481   GtkFileChooserWidgetPrivate *widget_priv;
482   GtkFileChooserDefault *impl;
483
484   d = GTK_FILE_CHOOSER_DIALOG (dialog);
485   dialog_priv = d->priv;
486   chooser_widget = GTK_FILE_CHOOSER_WIDGET (dialog_priv->widget);
487   if (!chooser_widget)
488     g_error ("BUG: dialog_priv->widget is not a GtkFileChooserWidget");
489
490   widget_priv = chooser_widget->priv;
491   impl = (GtkFileChooserDefault *) (widget_priv->impl);
492   if (!impl)
493     g_error ("BUG: widget_priv->impl is not a GtkFileChooserDefault");
494
495   return impl;
496 }
497
498 static gboolean
499 test_widgets_for_current_action (GtkFileChooserDialog *dialog,
500                                  GtkFileChooserAction  expected_action)
501 {
502   GtkFileChooserDefault *impl;
503   gboolean passed;
504
505   if (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) != expected_action)
506     return FALSE;
507
508   impl = get_impl_from_dialog (GTK_WIDGET (dialog));
509
510   g_assert (impl->action == expected_action);
511
512   passed = TRUE;
513
514   /* OPEN implies that the "new folder" button is hidden; otherwise it is shown */
515   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
516     passed = passed && !gtk_widget_get_visible (impl->browse_new_folder_button);
517   else
518     passed = passed && gtk_widget_get_visible (impl->browse_new_folder_button);
519
520   /* Check that the widgets are present/visible or not */
521   if (has_action (open_actions, G_N_ELEMENTS (open_actions), impl->action))
522     {
523       passed = passed && (impl->save_widgets == NULL
524                           && (impl->location_mode == LOCATION_MODE_PATH_BAR
525                               ? impl->location_entry == NULL
526                               : impl->location_entry != NULL)
527                           && impl->save_folder_label == NULL
528                           && impl->save_folder_combo == NULL
529                           && impl->save_expander == NULL
530                           && GTK_IS_CONTAINER (impl->browse_widgets) && gtk_widget_is_drawable (impl->browse_widgets));
531     }
532   else if (has_action (save_actions, G_N_ELEMENTS (save_actions), impl->action))
533     {
534       /* FIXME: we can't use GTK_IS_FILE_CHOOSER_ENTRY() because it uses
535        * _gtk_file_chooser_entry_get_type(), which is a non-exported symbol.
536        * So, we just test impl->location_entry for being non-NULL
537        */
538       passed = passed && (GTK_IS_CONTAINER (impl->save_widgets) && gtk_widget_is_drawable (impl->save_widgets)
539                           && impl->location_entry != NULL && gtk_widget_is_drawable (impl->location_entry)
540                           && GTK_IS_LABEL (impl->save_folder_label) && gtk_widget_is_drawable (impl->save_folder_label)
541                           && GTK_IS_COMBO_BOX (impl->save_folder_combo) && gtk_widget_is_drawable (impl->save_folder_combo)
542                           && GTK_IS_EXPANDER (impl->save_expander) && gtk_widget_is_drawable (impl->save_expander)
543                           && GTK_IS_CONTAINER (impl->browse_widgets));
544
545       /* FIXME: we are in a SAVE mode; test the visibility and sensitivity of
546        * the children that change depending on the state of the expander.
547        */
548     }
549   else
550     {
551       g_error ("BAD TEST: test_widgets_for_current_action() doesn't know about %s", get_action_name (impl->action));
552       passed = FALSE;
553     }
554
555   return passed;
556 }
557
558 typedef gboolean (* ForeachActionCallback) (GtkFileChooserDialog *dialog,
559                                             GtkFileChooserAction  action,
560                                             gpointer              user_data);
561
562 static gboolean
563 foreach_action (GtkFileChooserDialog *dialog,
564                 ForeachActionCallback callback,
565                 gpointer              user_data)
566 {
567   GEnumClass *enum_class;
568   int i;
569
570   enum_class = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
571   if (!enum_class)
572     g_error ("BUG: get_action_name(): no GEnumClass for GTK_TYPE_FILE_CHOOSER_ACTION");
573
574   for (i = 0; i < enum_class->n_values; i++)
575     {
576       GEnumValue *enum_value;
577       GtkFileChooserAction action;
578       gboolean passed;
579
580       enum_value = enum_class->values + i;
581       action = enum_value->value;
582
583       passed = (* callback) (dialog, action, user_data);
584       if (!passed)
585         return FALSE;
586     }
587
588   return TRUE;
589 }
590
591 struct action_closure {
592   GtkFileChooserAction from_action;
593 };
594
595 static gboolean
596 switch_from_to_action_cb (GtkFileChooserDialog *dialog,
597                           GtkFileChooserAction  action,
598                           gpointer              user_data)
599 {
600   struct action_closure *closure;
601   gboolean passed;
602
603   closure = user_data;
604
605   gtk_file_chooser_set_action (GTK_FILE_CHOOSER (dialog), closure->from_action);
606
607   passed = test_widgets_for_current_action (dialog, closure->from_action);
608   log_test (passed, "switch_from_to_action_cb(): reset to action %s", get_action_name (closure->from_action));
609   if (!passed)
610     return FALSE;
611
612   gtk_file_chooser_set_action (GTK_FILE_CHOOSER (dialog), action);
613
614   passed = test_widgets_for_current_action (dialog, action);
615   log_test (passed, "switch_from_to_action_cb(): transition from %s to %s",
616             get_action_name (closure->from_action),
617             get_action_name (action));
618   return passed;
619 }
620
621 static gboolean
622 switch_from_action_cb (GtkFileChooserDialog *dialog,
623                        GtkFileChooserAction  action,
624                        gpointer              user_data)
625 {
626   struct action_closure closure;
627
628   closure.from_action = action;
629
630   return foreach_action (dialog, switch_from_to_action_cb, &closure);
631 }
632
633 static void
634 test_action_widgets (void)
635 {
636   GtkWidget *dialog;
637   GtkFileChooserAction action;
638   gboolean passed;
639
640   dialog = gtk_file_chooser_dialog_new ("Test file chooser",
641                                         NULL,
642                                         GTK_FILE_CHOOSER_ACTION_OPEN,
643                                         GTK_STOCK_CANCEL,
644                                         GTK_RESPONSE_CANCEL,
645                                         GTK_STOCK_OK,
646                                         GTK_RESPONSE_ACCEPT,
647                                         NULL);
648   gtk_widget_show_now (dialog);
649
650   action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog));
651
652   passed = test_widgets_for_current_action (GTK_FILE_CHOOSER_DIALOG (dialog), action);
653   log_test (passed, "test_action_widgets(): widgets for initial action %s", get_action_name (action));
654   g_assert (passed);
655
656   passed = foreach_action (GTK_FILE_CHOOSER_DIALOG (dialog), switch_from_action_cb, NULL);
657   log_test (passed, "test_action_widgets(): all transitions through property change");
658   g_assert (passed);
659
660   gtk_widget_destroy (dialog);
661 }
662
663 static gboolean
664 test_reload_sequence (gboolean set_folder_before_map)
665 {
666   GtkWidget *dialog;
667   GtkFileChooserDefault *impl;
668   gboolean passed;
669   char *folder;
670   char *current_working_dir;
671
672   passed = TRUE;
673
674   current_working_dir = g_get_current_dir ();
675
676   dialog = gtk_file_chooser_dialog_new ("Test file chooser",
677                                         NULL,
678                                         GTK_FILE_CHOOSER_ACTION_OPEN,
679                                         GTK_STOCK_CANCEL,
680                                         GTK_RESPONSE_CANCEL,
681                                         GTK_STOCK_OK,
682                                         GTK_RESPONSE_ACCEPT,
683                                         NULL);
684   impl = get_impl_from_dialog (dialog);
685
686   if (set_folder_before_map)
687     {
688       gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), g_get_home_dir ());
689
690       wait_for_idle ();
691
692       passed = passed && (impl->current_folder != NULL
693                           && impl->browse_files_model != NULL
694                           && (impl->load_state == LOAD_PRELOAD || impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
695                           && impl->reload_state == RELOAD_HAS_FOLDER
696                           && (impl->load_state == LOAD_PRELOAD ? (impl->load_timeout_id != 0) : TRUE)
697                           && ((impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
698                               ? (impl->load_timeout_id == 0 && impl->sort_model != NULL)
699                               : TRUE));
700
701       wait_for_idle ();
702
703       folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
704       passed = passed && (g_strcmp0 (folder, g_get_home_dir()) == 0);
705       g_free (folder);
706     }
707   else
708     {
709       /* Initially, no folder is not loaded or pending */
710       passed = passed && (impl->current_folder == NULL
711                           && impl->sort_model == NULL
712                           && impl->browse_files_model == NULL
713                           && impl->load_state == LOAD_EMPTY
714                           && impl->reload_state == RELOAD_EMPTY
715                           && impl->load_timeout_id == 0);
716
717       wait_for_idle ();
718
719       folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
720       passed = passed && (g_strcmp0 (folder, current_working_dir) == 0);
721     }
722
723   log_test (passed, "test_reload_sequence(): initial status");
724
725   /* After mapping, it is loading some folder, either the one that was explicitly set or the default one */
726
727   gtk_widget_show_now (dialog);
728
729   wait_for_idle ();
730
731   passed = passed && (impl->current_folder != NULL
732                       && impl->browse_files_model != NULL
733                       && (impl->load_state == LOAD_PRELOAD || impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
734                       && impl->reload_state == RELOAD_HAS_FOLDER
735                       && (impl->load_state == LOAD_PRELOAD ? (impl->load_timeout_id != 0) : TRUE)
736                       && ((impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
737                           ? (impl->load_timeout_id == 0 && impl->sort_model != NULL)
738                           : TRUE));
739
740   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
741   if (set_folder_before_map)
742     passed = passed && (g_strcmp0 (folder, g_get_home_dir()) == 0);
743   else
744     passed = passed && (g_strcmp0 (folder, current_working_dir) == 0);
745
746   g_free (folder);
747
748   log_test (passed, "test_reload_sequence(): status after map");
749
750   /* Unmap it; we should still have a folder */
751
752   gtk_widget_hide (dialog);
753
754   wait_for_idle ();
755
756   passed = passed && (impl->current_folder != NULL
757                       && impl->browse_files_model != NULL
758                       && (impl->load_state == LOAD_PRELOAD || impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
759                       && (impl->load_state == LOAD_PRELOAD ? (impl->load_timeout_id != 0) : TRUE)
760                       && ((impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
761                           ? (impl->load_timeout_id == 0 && impl->sort_model != NULL)
762                           : TRUE));
763
764   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
765   if (set_folder_before_map)
766     passed = passed && (g_strcmp0 (folder, g_get_home_dir()) == 0);
767   else
768     passed = passed && (g_strcmp0 (folder, current_working_dir) == 0);
769
770   g_free (folder);
771
772   log_test (passed, "test_reload_sequence(): status after unmap");
773
774   /* Map it again! */
775
776   gtk_widget_show_now (dialog);
777
778   wait_for_idle ();
779
780   passed = passed && (impl->current_folder != NULL
781                       && impl->browse_files_model != NULL
782                       && (impl->load_state == LOAD_PRELOAD || impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
783                       && impl->reload_state == RELOAD_HAS_FOLDER
784                       && (impl->load_state == LOAD_PRELOAD ? (impl->load_timeout_id != 0) : TRUE)
785                       && ((impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
786                           ? (impl->load_timeout_id == 0 && impl->sort_model != NULL)
787                           : TRUE));
788
789   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
790   if (set_folder_before_map)
791     passed = passed && (g_strcmp0 (folder, g_get_home_dir()) == 0);
792   else
793     passed = passed && (g_strcmp0 (folder, current_working_dir) == 0);
794
795   g_free (folder);
796
797   log_test (passed, "test_reload_sequence(): status after re-map");
798
799   gtk_widget_destroy (dialog);
800   g_free (current_working_dir);
801
802   return passed;
803 }
804
805 static void
806 test_reload (void)
807 {
808   gboolean passed;
809
810   passed = test_reload_sequence (FALSE);
811   log_test (passed, "test_reload(): create and use the default folder");
812   g_assert (passed);
813
814   passed = test_reload_sequence (TRUE);
815   log_test (passed, "test_reload(): set a folder explicitly before mapping");
816   g_assert (passed);
817 }
818
819 static gboolean
820 test_button_folder_states_for_action (GtkFileChooserAction action, gboolean use_dialog, gboolean set_folder_on_dialog)
821 {
822   gboolean passed;
823   GtkWidget *window;
824   GtkWidget *button;
825   char *folder;
826   GtkWidget *dialog;
827   char *current_working_dir;
828   gboolean must_have_cwd;
829
830   passed = TRUE;
831
832   current_working_dir = g_get_current_dir ();
833   must_have_cwd = !(use_dialog && set_folder_on_dialog);
834
835   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
836
837   if (use_dialog)
838     {
839       dialog = gtk_file_chooser_dialog_new ("Test", NULL, action,
840                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
841                                             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
842                                             NULL);
843       button = gtk_file_chooser_button_new_with_dialog (dialog);
844
845       if (set_folder_on_dialog)
846         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), g_get_home_dir ());
847     }
848   else
849     {
850       button = gtk_file_chooser_button_new ("Test", action);
851       dialog = NULL; /* keep gcc happy */
852     }
853
854   gtk_container_add (GTK_CONTAINER (window), button);
855
856   /* Pre-map; no folder is set */
857   wait_for_idle ();
858
859   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (button));
860   if (must_have_cwd)
861     passed = passed && (g_strcmp0 (folder, current_working_dir) == 0);
862   else
863     passed = passed && (g_strcmp0 (folder, g_get_home_dir()) == 0);
864
865   log_test (passed, "test_button_folder_states_for_action(): %s, use_dialog=%d, set_folder_on_dialog=%d, pre-map, %s",
866             get_action_name (action),
867             use_dialog,
868             set_folder_on_dialog,
869             must_have_cwd ? "must have $cwd" : "must have explicit folder");
870
871   /* Map; folder should be set */
872
873   gtk_widget_show_all (window);
874   gtk_widget_show_now (window);
875
876   wait_for_idle ();
877
878   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (button));
879
880   if (must_have_cwd)
881     passed = passed && (g_strcmp0 (folder, current_working_dir) == 0);
882   else
883     passed = passed && (g_strcmp0 (folder, g_get_home_dir()) == 0);
884
885   log_test (passed, "test_button_folder_states_for_action(): %s, use_dialog=%d, set_folder_on_dialog=%d, mapped, %s",
886             get_action_name (action),
887             use_dialog,
888             set_folder_on_dialog,
889             must_have_cwd ? "must have $cwd" : "must have explicit folder");
890   g_free (folder);
891
892   /* Unmap; folder should be set */
893
894   gtk_widget_hide (window);
895   wait_for_idle ();
896   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (button));
897
898   if (must_have_cwd)
899     passed = passed && (g_strcmp0 (folder, current_working_dir) == 0);
900   else
901     passed = passed && (g_strcmp0 (folder, g_get_home_dir()) == 0);
902
903   log_test (passed, "test_button_folder_states_for_action(): %s, use_dialog=%d, set_folder_on_dialog=%d, unmapped, %s",
904             get_action_name (action),
905             use_dialog,
906             set_folder_on_dialog,
907             must_have_cwd ? "must have $cwd" : "must have explicit folder");
908   g_free (folder);
909
910   /* Re-map; folder should be set */
911
912   gtk_widget_show_now (window);
913   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (button));
914
915   if (must_have_cwd)
916     passed = passed && (g_strcmp0 (folder, current_working_dir) == 0);
917   else
918     passed = passed && (g_strcmp0 (folder, g_get_home_dir()) == 0);
919   wait_for_idle ();
920   log_test (passed, "test_button_folder_states_for_action(): %s, use_dialog=%d, set_folder_on_dialog=%d, re-mapped, %s",
921             get_action_name (action),
922             use_dialog,
923             set_folder_on_dialog,
924             must_have_cwd ? "must have $cwd" : "must have explicit folder");
925   g_free (folder);
926
927   g_free (current_working_dir);
928
929   gtk_widget_destroy (window);
930
931   return passed;
932 }
933
934 static void
935 test_button_folder_states (void)
936 {
937   /* GtkFileChooserButton only supports OPEN and SELECT_FOLDER */
938   static const GtkFileChooserAction actions_to_test[] = {
939     GTK_FILE_CHOOSER_ACTION_OPEN,
940     GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
941   };
942   gboolean passed;
943   int i;
944
945   passed = TRUE;
946
947   for (i = 0; i < G_N_ELEMENTS (actions_to_test); i++)
948     {
949       passed = passed && test_button_folder_states_for_action (actions_to_test[i], FALSE, FALSE);
950       g_assert (passed);
951       passed = passed && test_button_folder_states_for_action (actions_to_test[i], TRUE, FALSE);
952       g_assert (passed);
953       passed = passed && test_button_folder_states_for_action (actions_to_test[i], TRUE, TRUE);
954       g_assert (passed);
955       log_test (passed, "test_button_folder_states(): action %s", get_action_name (actions_to_test[i]));
956     }
957
958   log_test (passed, "test_button_folder_states(): all supported actions");
959 }
960
961 static gboolean
962 sleep_timeout_cb (gpointer data)
963 {
964   gtk_main_quit ();
965   return FALSE;
966 }
967
968 static void
969 sleep_in_main_loop (double fraction)
970 {
971   /* process all pending idles and events */
972   while (g_main_context_pending (NULL))
973     g_main_context_iteration (NULL, FALSE);
974   /* sleeping probably isn't strictly necessary here */
975   gdk_threads_add_timeout_full (G_MAXINT, fraction * SLEEP_DURATION, sleep_timeout_cb, NULL, NULL);
976   gtk_main ();
977   /* process any pending idles or events that arrived during sleep */
978   while (g_main_context_pending (NULL))
979     g_main_context_iteration (NULL, FALSE);
980 }
981
982 static void
983 test_folder_switch_and_filters (void)
984 {
985   gboolean passed;
986   char *cwd;
987   char *base_dir;
988   GtkFilePath *cwd_path;
989   GtkFilePath *base_dir_path;
990   GtkWidget *dialog;
991   GtkFileFilter *all_filter;
992   GtkFileFilter *txt_filter;
993   GtkFileChooserDefault *impl;
994
995   passed = TRUE;
996
997   cwd = g_get_current_dir ();
998   base_dir = g_build_filename (cwd, "file-chooser-test-dir", NULL);
999
1000   dialog = gtk_file_chooser_dialog_new ("Test", NULL, GTK_FILE_CHOOSER_ACTION_OPEN,
1001                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1002                                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1003                                         NULL);
1004   impl = get_impl_from_dialog (dialog);
1005
1006   cwd_path = gtk_file_system_filename_to_path (impl->file_system, cwd);
1007   base_dir_path = gtk_file_system_filename_to_path (impl->file_system, base_dir);
1008
1009   passed = passed && gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), base_dir);
1010   g_assert (passed);
1011
1012   /* All files filter */
1013
1014   all_filter = gtk_file_filter_new ();
1015   gtk_file_filter_set_name (all_filter, "All files");
1016   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), all_filter);
1017
1018   /* *.txt filter */
1019
1020   txt_filter = gtk_file_filter_new ();
1021   gtk_file_filter_set_name (all_filter, "*.txt");
1022   gtk_file_filter_add_pattern (txt_filter, "*.txt");
1023   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), txt_filter);
1024
1025   /* Test filter set */
1026
1027   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), all_filter);
1028   passed = passed && (gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (dialog)) == all_filter);
1029   g_assert (passed);
1030
1031   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), txt_filter);
1032   passed = passed && (gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (dialog)) == txt_filter);
1033   log_test (passed, "test_folder_switch_and_filters(): set and get filter");
1034   g_assert (passed);
1035
1036   gtk_widget_show (dialog);
1037
1038   /* Test that filter is unchanged when we switch folders */
1039
1040   gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), cwd);
1041   sleep_in_main_loop (0.5);
1042   passed = passed && (gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (dialog)) == txt_filter);
1043   g_assert (passed);
1044
1045   gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), base_dir);
1046   sleep_in_main_loop (0.25);
1047
1048   g_signal_emit_by_name (impl->browse_path_bar, "path-clicked",
1049                          (GtkFilePath *) cwd_path,
1050                          (GtkFilePath *) base_dir_path,
1051                          FALSE);
1052   sleep_in_main_loop (0.25);
1053   passed = passed && (gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (dialog)) == txt_filter);
1054   log_test (passed, "test_folder_switch_and_filters(): filter after changing folder");
1055   g_assert (passed);
1056
1057   /* cleanups */
1058   g_free (cwd);
1059   g_free (base_dir);
1060   gtk_file_path_free (cwd_path);
1061   gtk_file_path_free (base_dir_path);
1062
1063   gtk_widget_destroy (dialog);
1064
1065   log_test (passed, "test_folder_switch_and_filters(): all filter tests");
1066 }
1067
1068 extern void pixbuf_init (void);
1069
1070 int
1071 main (int    argc,
1072       char **argv)
1073 {
1074   pixbuf_init ();
1075   /* initialize test program */
1076   gtk_test_init (&argc, &argv);
1077
1078   /* register tests */
1079   g_test_add_func ("/GtkFileChooser/black_box", test_black_box);
1080   g_test_add_func ("/GtkFileChooser/confirm_overwrite", test_confirm_overwrite);
1081   g_test_add_func ("/GtkFileChooser/action_widgets", test_action_widgets);
1082   g_test_add_func ("/GtkFileChooser/reload", test_reload);
1083   g_test_add_func ("/GtkFileChooser/button_folder_states", test_button_folder_states);
1084   g_test_add_func ("/GtkFileChooser/folder_switch_and_filters", test_folder_switch_and_filters);
1085
1086   /* run and check selected tests */
1087   return g_test_run();
1088 }