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