]> Pileus Git - ~andy/gtk/blob - tests/autotestfilechooser.c
Fixes bug #317999:
[~andy/gtk] / tests / autotestfilechooser.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 #define GTK_FILE_SYSTEM_ENABLE_UNSUPPORTED
25
26 #include <config.h>
27 #include <string.h>
28 #include <glib/gprintf.h>
29 #include <gtk/gtk.h>
30 #include "gtk/gtkfilechooserprivate.h"
31 #include "gtk/gtkfilechooserdefault.h"
32 #include "gtk/gtkfilechooserentry.h"
33
34 static void
35 log_test (gboolean passed, const char *test_name, ...)
36 {
37   va_list args;
38   char *str;
39
40   va_start (args, test_name);
41   str = g_strdup_vprintf (test_name, args);
42   va_end (args);
43
44   g_printf ("%s: %s\n", passed ? "PASSED" : "FAILED", str);
45   g_free (str);
46 }
47
48 static const GtkFileChooserAction open_actions[] = {
49   GTK_FILE_CHOOSER_ACTION_OPEN,
50   GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
51 };
52
53 static const GtkFileChooserAction save_actions[] = {
54   GTK_FILE_CHOOSER_ACTION_SAVE,
55   GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
56 };
57
58
59 static gboolean
60 has_action (const GtkFileChooserAction *actions,
61             int n_actions,
62             GtkFileChooserAction sought_action)
63 {
64   int i;
65
66   for (i = 0; i < n_actions; i++)
67     if (actions[i] == sought_action)
68       return TRUE;
69
70   return FALSE;
71 }
72
73 static const char *
74 get_action_name (GtkFileChooserAction action)
75 {
76   GEnumClass *enum_class;
77   GEnumValue *enum_value;
78
79   enum_class = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
80   if (!enum_class)
81     g_error ("BUG: get_action_name(): no GEnumClass for GTK_TYPE_FILE_CHOOSER_ACTION");
82
83   enum_value = g_enum_get_value (enum_class, (int) action);
84   if (!enum_value)
85     g_error ("BUG: get_action_name(): no GEnumValue for GtkFileChooserAction %d", (int) action);
86
87   return enum_value->value_name;
88 }
89
90 static GtkFileChooserDefault *
91 get_impl_from_dialog (GtkWidget *dialog)
92 {
93   GtkFileChooserDialog *d;
94   GtkFileChooserDialogPrivate *dialog_priv;
95   GtkFileChooserWidget *chooser_widget;
96   GtkFileChooserWidgetPrivate *widget_priv;
97   GtkFileChooserDefault *impl;
98
99   d = GTK_FILE_CHOOSER_DIALOG (dialog);
100   dialog_priv = d->priv;
101   chooser_widget = GTK_FILE_CHOOSER_WIDGET (dialog_priv->widget);
102   if (!chooser_widget)
103     g_error ("BUG: dialog_priv->widget is not a GtkFileChooserWidget");
104
105   widget_priv = chooser_widget->priv;
106   impl = (GtkFileChooserDefault *) (widget_priv->impl);
107   if (!impl)
108     g_error ("BUG: widget_priv->impl is not a GtkFileChooserDefault");
109
110   return impl;
111 }
112
113 static gboolean
114 test_widgets_for_current_action (GtkFileChooserDialog *dialog,
115                                  GtkFileChooserAction  expected_action)
116 {
117   GtkFileChooserDefault *impl;
118   gboolean passed;
119
120   if (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) != expected_action)
121     return FALSE;
122
123   impl = get_impl_from_dialog (GTK_WIDGET (dialog));
124
125   g_assert (impl->action == expected_action);
126
127   passed = TRUE;
128
129   /* OPEN implies that the "new folder" button is hidden; otherwise it is shown */
130   if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
131     passed = passed && !GTK_WIDGET_VISIBLE (impl->browse_new_folder_button);
132   else
133     passed = passed && GTK_WIDGET_VISIBLE (impl->browse_new_folder_button);
134
135   /* Check that the widgets are present/visible or not */
136   if (has_action (open_actions, G_N_ELEMENTS (open_actions), impl->action))
137     {
138       passed = passed && (impl->save_widgets == NULL
139                           && impl->save_file_name_entry == NULL
140                           && impl->save_folder_label == NULL
141                           && impl->save_folder_combo == NULL
142                           && impl->save_expander == NULL
143                           && GTK_IS_CONTAINER (impl->browse_widgets) && GTK_WIDGET_DRAWABLE (impl->browse_widgets));
144     }
145   else if (has_action (save_actions, G_N_ELEMENTS (save_actions), impl->action))
146     {
147       /* FIXME: we can't use GTK_IS_FILE_CHOOSER_ENTRY() because it uses
148        * _gtk_file_chooser_entry_get_type(), which is a non-exported symbol.
149        * So, we just test impl->save_file_name_entry for being non-NULL
150        */
151       passed = passed && (GTK_IS_CONTAINER (impl->save_widgets) && GTK_WIDGET_DRAWABLE (impl->save_widgets)
152                           && impl->save_file_name_entry != NULL && GTK_WIDGET_DRAWABLE (impl->save_file_name_entry)
153                           && GTK_IS_LABEL (impl->save_folder_label) && GTK_WIDGET_DRAWABLE (impl->save_folder_label)
154                           && GTK_IS_COMBO_BOX (impl->save_folder_combo) && GTK_WIDGET_DRAWABLE (impl->save_folder_combo)
155                           && GTK_IS_EXPANDER (impl->save_expander) && GTK_WIDGET_DRAWABLE (impl->save_expander)
156                           && GTK_IS_CONTAINER (impl->browse_widgets));
157
158       /* FIXME: we are in a SAVE mode; test the visibility and sensitivity of
159        * the children that change depending on the state of the expander.
160        */
161     }
162   else
163     {
164       g_error ("BAD TEST: test_widgets_for_current_action() doesn't know about %s", get_action_name (impl->action));
165       passed = FALSE;
166     }
167
168   return passed;
169 }
170
171 typedef gboolean (* ForeachActionCallback) (GtkFileChooserDialog *dialog,
172                                             GtkFileChooserAction  action,
173                                             gpointer              user_data);
174
175 static gboolean
176 foreach_action (GtkFileChooserDialog *dialog,
177                 ForeachActionCallback callback,
178                 gpointer              user_data)
179 {
180   GEnumClass *enum_class;
181   int i;
182
183   enum_class = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
184   if (!enum_class)
185     g_error ("BUG: get_action_name(): no GEnumClass for GTK_TYPE_FILE_CHOOSER_ACTION");
186
187   for (i = 0; i < enum_class->n_values; i++)
188     {
189       GEnumValue *enum_value;
190       GtkFileChooserAction action;
191       gboolean passed;
192
193       enum_value = enum_class->values + i;
194       action = enum_value->value;
195
196       passed = (* callback) (dialog, action, user_data);
197       if (!passed)
198         return FALSE;
199     }
200
201   return TRUE;
202 }
203
204 struct action_closure {
205   GtkFileChooserAction from_action;
206 };
207
208 static gboolean
209 switch_from_to_action_cb (GtkFileChooserDialog *dialog,
210                           GtkFileChooserAction  action,
211                           gpointer              user_data)
212 {
213   struct action_closure *closure;
214   gboolean passed;
215
216   closure = user_data;
217
218   gtk_file_chooser_set_action (GTK_FILE_CHOOSER (dialog), closure->from_action);
219
220   passed = test_widgets_for_current_action (dialog, closure->from_action);
221   log_test (passed, "switch_from_to_action_cb(): reset to action %s", get_action_name (closure->from_action));
222   if (!passed)
223     return FALSE;
224
225   gtk_file_chooser_set_action (GTK_FILE_CHOOSER (dialog), action);
226
227   passed = test_widgets_for_current_action (dialog, action);
228   log_test (passed, "switch_from_to_action_cb(): transition from %s to %s",
229             get_action_name (closure->from_action),
230             get_action_name (action));
231   return passed;
232 }
233
234 static gboolean
235 switch_from_action_cb (GtkFileChooserDialog *dialog,
236                        GtkFileChooserAction  action,
237                        gpointer              user_data)
238 {
239   struct action_closure closure;
240
241   closure.from_action = action;
242
243   return foreach_action (dialog, switch_from_to_action_cb, &closure);
244 }
245
246 static gboolean
247 test_action_widgets (void)
248 {
249   GtkWidget *dialog;
250   GtkFileChooserAction action;
251   gboolean passed;
252
253   dialog = gtk_file_chooser_dialog_new ("Test file chooser",
254                                         NULL,
255                                         GTK_FILE_CHOOSER_ACTION_OPEN,
256                                         GTK_STOCK_CANCEL,
257                                         GTK_RESPONSE_CANCEL,
258                                         GTK_STOCK_OK,
259                                         GTK_RESPONSE_ACCEPT,
260                                         NULL);
261   gtk_widget_show_now (dialog);
262
263   action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog));
264
265   passed = test_widgets_for_current_action (GTK_FILE_CHOOSER_DIALOG (dialog), action);
266   log_test (passed, "test_action_widgets(): widgets for initial action %s", get_action_name (action));
267   if (!passed)
268     return FALSE;
269
270   passed = foreach_action (GTK_FILE_CHOOSER_DIALOG (dialog), switch_from_action_cb, NULL);
271   log_test (passed, "test_action_widgets(): all transitions through property change");
272
273   gtk_widget_destroy (dialog);
274
275   return passed;
276 }
277
278 static gboolean
279 test_reload_sequence (gboolean set_folder_before_map)
280 {
281   GtkWidget *dialog;
282   GtkFileChooserDefault *impl;
283   gboolean passed;
284   char *folder;
285   char *current_working_dir;
286
287   passed = TRUE;
288
289   current_working_dir = g_get_current_dir ();
290
291   dialog = gtk_file_chooser_dialog_new ("Test file chooser",
292                                         NULL,
293                                         GTK_FILE_CHOOSER_ACTION_OPEN,
294                                         GTK_STOCK_CANCEL,
295                                         GTK_RESPONSE_CANCEL,
296                                         GTK_STOCK_OK,
297                                         GTK_RESPONSE_ACCEPT,
298                                         NULL);
299   impl = get_impl_from_dialog (dialog);
300
301   if (set_folder_before_map)
302     {
303       gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), g_get_home_dir ());
304
305       passed = passed && (impl->current_folder != NULL
306                           && impl->browse_files_model != NULL
307                           && (impl->load_state == LOAD_PRELOAD || impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
308                           && impl->reload_state == RELOAD_HAS_FOLDER
309                           && (impl->load_state == LOAD_PRELOAD ? (impl->load_timeout_id != 0) : TRUE)
310                           && ((impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
311                               ? (impl->load_timeout_id == 0 && impl->sort_model != NULL)
312                               : TRUE));
313
314       folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
315       passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
316       g_free (folder);
317     }
318   else
319     {
320       /* Initially, no folder is not loaded or pending */
321       passed = passed && (impl->current_folder == NULL
322                           && impl->sort_model == NULL
323                           && impl->browse_files_model == NULL
324                           && impl->load_state == LOAD_EMPTY
325                           && impl->reload_state == RELOAD_EMPTY
326                           && impl->load_timeout_id == 0);
327
328       folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
329       passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
330     }
331
332   log_test (passed, "test_reload_sequence(): initial status");
333
334   /* After mapping, it is loading some folder, either the one that was explicitly set or the default one */
335
336   gtk_widget_show_now (dialog);
337
338   passed = passed && (impl->current_folder != NULL
339                       && impl->browse_files_model != NULL
340                       && (impl->load_state == LOAD_PRELOAD || impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
341                       && impl->reload_state == RELOAD_HAS_FOLDER
342                       && (impl->load_state == LOAD_PRELOAD ? (impl->load_timeout_id != 0) : TRUE)
343                       && ((impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
344                           ? (impl->load_timeout_id == 0 && impl->sort_model != NULL)
345                           : TRUE));
346
347   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
348   if (set_folder_before_map)
349     passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
350   else
351     passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
352
353   g_free (folder);
354
355   log_test (passed, "test_reload_sequence(): status after map");
356
357   /* Unmap it; we should still have a folder */
358
359   gtk_widget_hide (dialog);
360
361   passed = passed && (impl->current_folder != NULL
362                       && impl->browse_files_model != NULL
363                       && (impl->load_state == LOAD_PRELOAD || impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
364                       && impl->reload_state == RELOAD_WAS_UNMAPPED
365                       && (impl->load_state == LOAD_PRELOAD ? (impl->load_timeout_id != 0) : TRUE)
366                       && ((impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
367                           ? (impl->load_timeout_id == 0 && impl->sort_model != NULL)
368                           : TRUE));
369
370   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
371   if (set_folder_before_map)
372     passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
373   else
374     passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
375
376   g_free (folder);
377
378   log_test (passed, "test_reload_sequence(): status after unmap");
379
380   /* Map it again! */
381
382   gtk_widget_show_now (dialog);
383
384   passed = passed && (impl->current_folder != NULL
385                       && impl->browse_files_model != NULL
386                       && (impl->load_state == LOAD_PRELOAD || impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
387                       && impl->reload_state == RELOAD_HAS_FOLDER
388                       && (impl->load_state == LOAD_PRELOAD ? (impl->load_timeout_id != 0) : TRUE)
389                       && ((impl->load_state == LOAD_LOADING || impl->load_state == LOAD_FINISHED)
390                           ? (impl->load_timeout_id == 0 && impl->sort_model != NULL)
391                           : TRUE));
392
393   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
394   if (set_folder_before_map)
395     passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
396   else
397     passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
398
399   g_free (folder);
400
401   log_test (passed, "test_reload_sequence(): status after re-map");
402
403   gtk_widget_destroy (dialog);
404   g_free (current_working_dir);
405
406   return passed;
407 }
408
409 static gboolean
410 test_reload (void)
411 {
412   gboolean passed;
413
414   passed = test_reload_sequence (FALSE);
415   log_test (passed, "test_reload(): create and use the default folder");
416   if (!passed)
417     return FALSE;
418
419   passed = test_reload_sequence (TRUE);
420   log_test (passed, "test_reload(): set a folder explicitly before mapping");
421
422   return passed;
423 }
424
425 static gboolean
426 test_button_folder_states_for_action (GtkFileChooserAction action, gboolean use_dialog, gboolean set_folder_on_dialog)
427 {
428   gboolean passed;
429   GtkWidget *window;
430   GtkWidget *button;
431   char *folder;
432   GtkWidget *dialog;
433   char *current_working_dir;
434   gboolean must_have_cwd;
435
436   passed = TRUE;
437
438   current_working_dir = g_get_current_dir ();
439   must_have_cwd = !(use_dialog && set_folder_on_dialog);
440
441   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
442
443   if (use_dialog)
444     {
445       dialog = gtk_file_chooser_dialog_new ("Test", NULL, action,
446                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
447                                             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
448                                             NULL);
449       button = gtk_file_chooser_button_new_with_dialog (dialog);
450
451       if (set_folder_on_dialog)
452         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), g_get_home_dir ());
453     }
454   else
455     {
456       button = gtk_file_chooser_button_new ("Test", action);
457       dialog = NULL; /* keep gcc happy */
458     }
459
460   gtk_container_add (GTK_CONTAINER (window), button);
461
462   /* Pre-map; no folder is set */
463
464   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (button));
465   if (must_have_cwd)
466     passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
467   else
468     passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
469
470   log_test (passed, "test_button_folder_states_for_action(): %s, use_dialog=%d, set_folder_on_dialog=%d, pre-map, %s",
471             get_action_name (action),
472             use_dialog,
473             set_folder_on_dialog,
474             must_have_cwd ? "must have $cwd" : "must have explicit folder");
475
476   /* Map; folder should be set */
477
478   gtk_widget_show_all (window);
479   gtk_widget_show_now (window);
480   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (button));
481
482   if (must_have_cwd)
483     passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
484   else
485     passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
486
487   log_test (passed, "test_button_folder_states_for_action(): %s, use_dialog=%d, set_folder_on_dialog=%d, mapped, %s",
488             get_action_name (action),
489             use_dialog,
490             set_folder_on_dialog,
491             must_have_cwd ? "must have $cwd" : "must have explicit folder");
492   g_free (folder);
493
494   /* Unmap; folder should be set */
495
496   gtk_widget_hide (window);
497   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (button));
498
499   if (must_have_cwd)
500     passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
501   else
502     passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
503
504   log_test (passed, "test_button_folder_states_for_action(): %s, use_dialog=%d, set_folder_on_dialog=%d, unmapped, %s",
505             get_action_name (action),
506             use_dialog,
507             set_folder_on_dialog,
508             must_have_cwd ? "must have $cwd" : "must have explicit folder");
509   g_free (folder);
510
511   /* Re-map; folder should be set */
512
513   gtk_widget_show_now (window);
514   folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (button));
515
516   if (must_have_cwd)
517     passed = passed && (folder != NULL && strcmp (folder, current_working_dir) == 0);
518   else
519     passed = passed && (folder != NULL && strcmp (folder, g_get_home_dir()) == 0);
520
521   log_test (passed, "test_button_folder_states_for_action(): %s, use_dialog=%d, set_folder_on_dialog=%d, re-mapped, %s",
522             get_action_name (action),
523             use_dialog,
524             set_folder_on_dialog,
525             must_have_cwd ? "must have $cwd" : "must have explicit folder");
526   g_free (folder);
527
528   g_free (current_working_dir);
529
530   gtk_widget_destroy (window);
531
532   return passed;
533 }
534
535 static gboolean
536 test_button_folder_states (void)
537 {
538   /* GtkFileChooserButton only supports OPEN and SELECT_FOLDER */
539   static const GtkFileChooserAction actions_to_test[] = {
540     GTK_FILE_CHOOSER_ACTION_OPEN,
541     GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
542   };
543   gboolean passed;
544   int i;
545
546   passed = TRUE;
547
548   for (i = 0; i < G_N_ELEMENTS (actions_to_test); i++)
549     {
550       passed = passed && test_button_folder_states_for_action (actions_to_test[i], FALSE, FALSE);
551       passed = passed && test_button_folder_states_for_action (actions_to_test[i], TRUE, FALSE);
552       passed = passed && test_button_folder_states_for_action (actions_to_test[i], TRUE, TRUE);
553       log_test (passed, "test_button_folder_states(): action %s", get_action_name (actions_to_test[i]));
554     }
555
556   log_test (passed, "test_button_folder_states(): all supported actions");
557   return passed;
558 }
559
560 static int num_warnings;
561 static int num_errors;
562 static int num_critical_errors;
563
564 static void
565 log_override_cb (const gchar   *log_domain,
566                  GLogLevelFlags log_level,
567                  const gchar   *message,
568                  gpointer       user_data)
569 {
570   if (log_level & G_LOG_LEVEL_WARNING)
571     num_warnings++;
572
573   if (log_level & G_LOG_LEVEL_ERROR)
574     num_errors++;
575
576   if (log_level & G_LOG_LEVEL_CRITICAL)
577     num_critical_errors++;
578
579   g_log_default_handler (log_domain, log_level, message, user_data);
580 }
581
582 int
583 main (int argc, char **argv)
584 {
585   static const char *domains[] = {
586     "Glib", "GLib-GObject", "GModule", "GThread", "Pango", "Gdk", "GdkPixbuf", "Gtk", "libgnomevfs"
587   };
588
589   gboolean passed;
590   gboolean zero_warnings;
591   gboolean zero_errors;
592   gboolean zero_critical_errors;
593   int i;
594
595   for (i = 0; i < G_N_ELEMENTS (domains); i++)
596     g_log_set_handler (domains[i],
597                        G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL | G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING,
598                        log_override_cb, NULL);
599
600   gtk_init (&argc, &argv);
601
602   passed = test_action_widgets ();
603   passed = passed && test_reload ();
604   passed = passed && test_button_folder_states ();
605   log_test (passed, "main(): main tests");
606
607   zero_warnings = num_warnings == 0;
608   zero_errors = num_errors == 0;
609   zero_critical_errors = num_critical_errors == 0;
610
611   log_test (zero_warnings, "main(): zero warnings (actual number %d)", num_warnings);
612   log_test (zero_errors, "main(): zero errors (actual number %d)", num_errors);
613   log_test (zero_critical_errors, "main(): zero critical errors (actual number %d)", num_critical_errors);
614
615   passed = passed && zero_warnings && zero_errors && zero_critical_errors;
616
617   log_test (passed, "main(): ALL TESTS");
618
619   return 0;
620 }