1 /* GTK - The GIMP Toolkit
2 * autotestfilechooser.c: Automated unit tests for the GtkFileChooser widget
3 * Copyright (C) 2005, Novell, Inc.
6 * Federico Mena-Quintero <federico@novell.com>
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.
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.
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.
26 * - Use g_log_set_default_handler() instead of the mess of specific handlers we
29 * - In test_reload_sequence(), test that the selection is preserved properly
35 #define GTK_FILE_SYSTEM_ENABLE_UNSUPPORTED
39 #include <glib/gprintf.h>
41 #include "gtk/gtkfilechooserprivate.h"
42 #include "gtk/gtkfilechooserdefault.h"
43 #include "gtk/gtkfilechooserentry.h"
46 log_test (gboolean passed, const char *test_name, ...)
51 va_start (args, test_name);
52 str = g_strdup_vprintf (test_name, args);
55 g_printf ("%s: %s\n", passed ? "PASSED" : "FAILED", str);
59 static const GtkFileChooserAction open_actions[] = {
60 GTK_FILE_CHOOSER_ACTION_OPEN,
61 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
64 static const GtkFileChooserAction save_actions[] = {
65 GTK_FILE_CHOOSER_ACTION_SAVE,
66 GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
71 has_action (const GtkFileChooserAction *actions,
73 GtkFileChooserAction sought_action)
77 for (i = 0; i < n_actions; i++)
78 if (actions[i] == sought_action)
85 get_action_name (GtkFileChooserAction action)
87 GEnumClass *enum_class;
88 GEnumValue *enum_value;
90 enum_class = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
92 g_error ("BUG: get_action_name(): no GEnumClass for GTK_TYPE_FILE_CHOOSER_ACTION");
94 enum_value = g_enum_get_value (enum_class, (int) action);
96 g_error ("BUG: get_action_name(): no GEnumValue for GtkFileChooserAction %d", (int) action);
98 return enum_value->value_name;
101 static GtkFileChooserDefault *
102 get_impl_from_dialog (GtkWidget *dialog)
104 GtkFileChooserDialog *d;
105 GtkFileChooserDialogPrivate *dialog_priv;
106 GtkFileChooserWidget *chooser_widget;
107 GtkFileChooserWidgetPrivate *widget_priv;
108 GtkFileChooserDefault *impl;
110 d = GTK_FILE_CHOOSER_DIALOG (dialog);
111 dialog_priv = d->priv;
112 chooser_widget = GTK_FILE_CHOOSER_WIDGET (dialog_priv->widget);
114 g_error ("BUG: dialog_priv->widget is not a GtkFileChooserWidget");
116 widget_priv = chooser_widget->priv;
117 impl = (GtkFileChooserDefault *) (widget_priv->impl);
119 g_error ("BUG: widget_priv->impl is not a GtkFileChooserDefault");
125 test_widgets_for_current_action (GtkFileChooserDialog *dialog,
126 GtkFileChooserAction expected_action)
128 GtkFileChooserDefault *impl;
131 if (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) != expected_action)
134 impl = get_impl_from_dialog (GTK_WIDGET (dialog));
136 g_assert (impl->action == expected_action);
140 /* OPEN implies that the "new folder" button is hidden; otherwise it is shown */
141 if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
142 passed = passed && !GTK_WIDGET_VISIBLE (impl->browse_new_folder_button);
144 passed = passed && GTK_WIDGET_VISIBLE (impl->browse_new_folder_button);
146 /* Check that the widgets are present/visible or not */
147 if (has_action (open_actions, G_N_ELEMENTS (open_actions), impl->action))
149 passed = passed && (impl->save_widgets == NULL
150 && (impl->location_mode == LOCATION_MODE_PATH_BAR
151 ? impl->location_entry == NULL
152 : impl->location_entry != NULL)
153 && impl->save_folder_label == NULL
154 && impl->save_folder_combo == NULL
155 && impl->save_expander == NULL
156 && GTK_IS_CONTAINER (impl->browse_widgets) && GTK_WIDGET_DRAWABLE (impl->browse_widgets));
158 else if (has_action (save_actions, G_N_ELEMENTS (save_actions), impl->action))
160 /* FIXME: we can't use GTK_IS_FILE_CHOOSER_ENTRY() because it uses
161 * _gtk_file_chooser_entry_get_type(), which is a non-exported symbol.
162 * So, we just test impl->location_entry for being non-NULL
164 passed = passed && (GTK_IS_CONTAINER (impl->save_widgets) && GTK_WIDGET_DRAWABLE (impl->save_widgets)
165 && impl->location_entry != NULL && GTK_WIDGET_DRAWABLE (impl->location_entry)
166 && GTK_IS_LABEL (impl->save_folder_label) && GTK_WIDGET_DRAWABLE (impl->save_folder_label)
167 && GTK_IS_COMBO_BOX (impl->save_folder_combo) && GTK_WIDGET_DRAWABLE (impl->save_folder_combo)
168 && GTK_IS_EXPANDER (impl->save_expander) && GTK_WIDGET_DRAWABLE (impl->save_expander)
169 && GTK_IS_CONTAINER (impl->browse_widgets));
171 /* FIXME: we are in a SAVE mode; test the visibility and sensitivity of
172 * the children that change depending on the state of the expander.
177 g_error ("BAD TEST: test_widgets_for_current_action() doesn't know about %s", get_action_name (impl->action));
184 typedef gboolean (* ForeachActionCallback) (GtkFileChooserDialog *dialog,
185 GtkFileChooserAction action,
189 foreach_action (GtkFileChooserDialog *dialog,
190 ForeachActionCallback callback,
193 GEnumClass *enum_class;
196 enum_class = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
198 g_error ("BUG: get_action_name(): no GEnumClass for GTK_TYPE_FILE_CHOOSER_ACTION");
200 for (i = 0; i < enum_class->n_values; i++)
202 GEnumValue *enum_value;
203 GtkFileChooserAction action;
206 enum_value = enum_class->values + i;
207 action = enum_value->value;
209 passed = (* callback) (dialog, action, user_data);
217 struct action_closure {
218 GtkFileChooserAction from_action;
222 switch_from_to_action_cb (GtkFileChooserDialog *dialog,
223 GtkFileChooserAction action,
226 struct action_closure *closure;
231 gtk_file_chooser_set_action (GTK_FILE_CHOOSER (dialog), closure->from_action);
233 passed = test_widgets_for_current_action (dialog, closure->from_action);
234 log_test (passed, "switch_from_to_action_cb(): reset to action %s", get_action_name (closure->from_action));
238 gtk_file_chooser_set_action (GTK_FILE_CHOOSER (dialog), action);
240 passed = test_widgets_for_current_action (dialog, action);
241 log_test (passed, "switch_from_to_action_cb(): transition from %s to %s",
242 get_action_name (closure->from_action),
243 get_action_name (action));
248 switch_from_action_cb (GtkFileChooserDialog *dialog,
249 GtkFileChooserAction action,
252 struct action_closure closure;
254 closure.from_action = action;
256 return foreach_action (dialog, switch_from_to_action_cb, &closure);
260 test_action_widgets (void)
263 GtkFileChooserAction action;
266 dialog = gtk_file_chooser_dialog_new ("Test file chooser",
268 GTK_FILE_CHOOSER_ACTION_OPEN,
274 gtk_widget_show_now (dialog);
276 action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog));
278 passed = test_widgets_for_current_action (GTK_FILE_CHOOSER_DIALOG (dialog), action);
279 log_test (passed, "test_action_widgets(): widgets for initial action %s", get_action_name (action));
283 passed = foreach_action (GTK_FILE_CHOOSER_DIALOG (dialog), switch_from_action_cb, NULL);
284 log_test (passed, "test_action_widgets(): all transitions through property change");
286 gtk_widget_destroy (dialog);
292 test_reload_sequence (gboolean set_folder_before_map)
295 GtkFileChooserDefault *impl;
298 char *current_working_dir;
302 current_working_dir = g_get_current_dir ();
304 dialog = gtk_file_chooser_dialog_new ("Test file chooser",
306 GTK_FILE_CHOOSER_ACTION_OPEN,
312 impl = get_impl_from_dialog (dialog);
314 if (set_folder_before_map)
316 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), g_get_home_dir ());
318 passed = passed && (impl->current_folder != NULL
319 && impl->browse_files_model != NULL
320 && (impl->load_state == LOAD_PRELOAD || impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
321 && impl->reload_state == RELOAD_HAS_FOLDER
322 && (impl->load_state == LOAD_PRELOAD ? (impl->load_timeout_id != 0) : TRUE)
323 && ((impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
324 ? (impl->load_timeout_id == 0 && impl->sort_model != NULL)
327 folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
328 passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
333 /* Initially, no folder is not loaded or pending */
334 passed = passed && (impl->current_folder == NULL
335 && impl->sort_model == NULL
336 && impl->browse_files_model == NULL
337 && impl->load_state == LOAD_EMPTY
338 && impl->reload_state == RELOAD_EMPTY
339 && impl->load_timeout_id == 0);
341 folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
342 passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
345 log_test (passed, "test_reload_sequence(): initial status");
347 /* After mapping, it is loading some folder, either the one that was explicitly set or the default one */
349 gtk_widget_show_now (dialog);
351 passed = passed && (impl->current_folder != NULL
352 && impl->browse_files_model != NULL
353 && (impl->load_state == LOAD_PRELOAD || impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
354 && impl->reload_state == RELOAD_HAS_FOLDER
355 && (impl->load_state == LOAD_PRELOAD ? (impl->load_timeout_id != 0) : TRUE)
356 && ((impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
357 ? (impl->load_timeout_id == 0 && impl->sort_model != NULL)
360 folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
361 if (set_folder_before_map)
362 passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
364 passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
368 log_test (passed, "test_reload_sequence(): status after map");
370 /* Unmap it; we should still have a folder */
372 gtk_widget_hide (dialog);
374 passed = passed && (impl->current_folder != NULL
375 && impl->browse_files_model != NULL
376 && (impl->load_state == LOAD_PRELOAD || impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
377 && impl->reload_state == RELOAD_WAS_UNMAPPED
378 && (impl->load_state == LOAD_PRELOAD ? (impl->load_timeout_id != 0) : TRUE)
379 && ((impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
380 ? (impl->load_timeout_id == 0 && impl->sort_model != NULL)
383 folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
384 if (set_folder_before_map)
385 passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
387 passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
391 log_test (passed, "test_reload_sequence(): status after unmap");
395 gtk_widget_show_now (dialog);
397 passed = passed && (impl->current_folder != NULL
398 && impl->browse_files_model != NULL
399 && (impl->load_state == LOAD_PRELOAD || impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
400 && impl->reload_state == RELOAD_HAS_FOLDER
401 && (impl->load_state == LOAD_PRELOAD ? (impl->load_timeout_id != 0) : TRUE)
402 && ((impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
403 ? (impl->load_timeout_id == 0 && impl->sort_model != NULL)
406 folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
407 if (set_folder_before_map)
408 passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
410 passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
414 log_test (passed, "test_reload_sequence(): status after re-map");
416 gtk_widget_destroy (dialog);
417 g_free (current_working_dir);
427 passed = test_reload_sequence (FALSE);
428 log_test (passed, "test_reload(): create and use the default folder");
432 passed = test_reload_sequence (TRUE);
433 log_test (passed, "test_reload(): set a folder explicitly before mapping");
439 test_button_folder_states_for_action (GtkFileChooserAction action, gboolean use_dialog, gboolean set_folder_on_dialog)
446 char *current_working_dir;
447 gboolean must_have_cwd;
451 current_working_dir = g_get_current_dir ();
452 must_have_cwd = !(use_dialog && set_folder_on_dialog);
454 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
458 dialog = gtk_file_chooser_dialog_new ("Test", NULL, action,
459 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
460 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
462 button = gtk_file_chooser_button_new_with_dialog (dialog);
464 if (set_folder_on_dialog)
465 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), g_get_home_dir ());
469 button = gtk_file_chooser_button_new ("Test", action);
470 dialog = NULL; /* keep gcc happy */
473 gtk_container_add (GTK_CONTAINER (window), button);
475 /* Pre-map; no folder is set */
477 folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (button));
479 passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
481 passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
483 log_test (passed, "test_button_folder_states_for_action(): %s, use_dialog=%d, set_folder_on_dialog=%d, pre-map, %s",
484 get_action_name (action),
486 set_folder_on_dialog,
487 must_have_cwd ? "must have $cwd" : "must have explicit folder");
489 /* Map; folder should be set */
491 gtk_widget_show_all (window);
492 gtk_widget_show_now (window);
493 folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (button));
496 passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
498 passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
500 log_test (passed, "test_button_folder_states_for_action(): %s, use_dialog=%d, set_folder_on_dialog=%d, mapped, %s",
501 get_action_name (action),
503 set_folder_on_dialog,
504 must_have_cwd ? "must have $cwd" : "must have explicit folder");
507 /* Unmap; folder should be set */
509 gtk_widget_hide (window);
510 folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (button));
513 passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
515 passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
517 log_test (passed, "test_button_folder_states_for_action(): %s, use_dialog=%d, set_folder_on_dialog=%d, unmapped, %s",
518 get_action_name (action),
520 set_folder_on_dialog,
521 must_have_cwd ? "must have $cwd" : "must have explicit folder");
524 /* Re-map; folder should be set */
526 gtk_widget_show_now (window);
527 folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (button));
530 passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
532 passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
534 log_test (passed, "test_button_folder_states_for_action(): %s, use_dialog=%d, set_folder_on_dialog=%d, re-mapped, %s",
535 get_action_name (action),
537 set_folder_on_dialog,
538 must_have_cwd ? "must have $cwd" : "must have explicit folder");
541 g_free (current_working_dir);
543 gtk_widget_destroy (window);
549 test_button_folder_states (void)
551 /* GtkFileChooserButton only supports OPEN and SELECT_FOLDER */
552 static const GtkFileChooserAction actions_to_test[] = {
553 GTK_FILE_CHOOSER_ACTION_OPEN,
554 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
561 for (i = 0; i < G_N_ELEMENTS (actions_to_test); i++)
563 passed = passed && test_button_folder_states_for_action (actions_to_test[i], FALSE, FALSE);
564 passed = passed && test_button_folder_states_for_action (actions_to_test[i], TRUE, FALSE);
565 passed = passed && test_button_folder_states_for_action (actions_to_test[i], TRUE, TRUE);
566 log_test (passed, "test_button_folder_states(): action %s", get_action_name (actions_to_test[i]));
569 log_test (passed, "test_button_folder_states(): all supported actions");
574 sleep_timeout_cb (gpointer data)
581 sleep_in_main_loop (int milliseconds)
583 g_timeout_add (milliseconds, sleep_timeout_cb, NULL);
588 test_folder_switch_and_filters (void)
593 GtkFilePath *cwd_path;
594 GtkFilePath *base_dir_path;
596 GtkFileFilter *all_filter;
597 GtkFileFilter *txt_filter;
598 GtkFileChooserDefault *impl;
602 cwd = g_get_current_dir ();
603 base_dir = g_build_filename (cwd, "file-chooser-test-dir", NULL);
605 dialog = gtk_file_chooser_dialog_new ("Test", NULL, GTK_FILE_CHOOSER_ACTION_OPEN,
606 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
607 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
609 impl = get_impl_from_dialog (dialog);
611 cwd_path = gtk_file_system_filename_to_path (impl->file_system, cwd);
612 base_dir_path = gtk_file_system_filename_to_path (impl->file_system, base_dir);
614 passed = passed && gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), base_dir);
618 /* All files filter */
620 all_filter = gtk_file_filter_new ();
621 gtk_file_filter_set_name (all_filter, "All files");
622 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), all_filter);
626 txt_filter = gtk_file_filter_new ();
627 gtk_file_filter_set_name (all_filter, "*.txt");
628 gtk_file_filter_add_pattern (txt_filter, "*.txt");
629 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), txt_filter);
631 /* Test filter set */
633 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), all_filter);
634 passed = passed && (gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (dialog)) == all_filter);
636 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), txt_filter);
637 passed = passed && (gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (dialog)) == txt_filter);
639 log_test (passed, "test_folder_switch_and_filters(): set and get filter");
641 gtk_widget_show (dialog);
643 /* Test that filter is unchanged when we switch folders */
645 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), cwd);
646 sleep_in_main_loop (1000);
647 passed = passed && (gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (dialog)) == txt_filter);
649 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), base_dir);
650 sleep_in_main_loop (500);
652 g_signal_emit_by_name (impl->browse_path_bar, "path-clicked",
653 (GtkFilePath *) cwd_path,
654 (GtkFilePath *) base_dir_path,
656 sleep_in_main_loop (500);
657 passed = passed && (gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (dialog)) == txt_filter);
659 log_test (passed, "test_folder_switch_and_filters(): filter after changing folder");
664 gtk_file_path_free (cwd_path);
665 gtk_file_path_free (base_dir_path);
667 gtk_widget_destroy (dialog);
669 log_test (passed, "test_folder_switch_and_filters(): all filter tests");
673 static GLogFunc default_log_handler;
674 static int num_warnings;
675 static int num_errors;
676 static int num_critical_errors;
679 log_override_cb (const gchar *log_domain,
680 GLogLevelFlags log_level,
681 const gchar *message,
684 if (log_level & G_LOG_LEVEL_WARNING)
687 if (log_level & G_LOG_LEVEL_ERROR)
690 if (log_level & G_LOG_LEVEL_CRITICAL)
691 num_critical_errors++;
693 (* default_log_handler) (log_domain, log_level, message, user_data);
697 main (int argc, char **argv)
700 gboolean zero_warnings;
701 gboolean zero_errors;
702 gboolean zero_critical_errors;
704 default_log_handler = g_log_set_default_handler (log_override_cb, NULL);
707 gtk_init (&argc, &argv);
710 passed = passed && test_action_widgets ();
711 passed = passed && test_reload ();
712 passed = passed && test_button_folder_states ();
713 passed = passed && test_folder_switch_and_filters ();
714 log_test (passed, "main(): main tests");
716 /* Warnings and errors */
718 zero_warnings = num_warnings == 0;
719 zero_errors = num_errors == 0;
720 zero_critical_errors = num_critical_errors == 0;
722 log_test (zero_warnings, "main(): zero warnings (actual number %d)", num_warnings);
723 log_test (zero_errors, "main(): zero errors (actual number %d)", num_errors);
724 log_test (zero_critical_errors, "main(): zero critical errors (actual number %d)", num_critical_errors);
728 passed = passed && zero_warnings && zero_errors && zero_critical_errors;
730 log_test (passed, "main(): ALL TESTS");