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