r19512: Add --shell option to smbtorture (first work towards support for 'recipes')
[gd/samba-autobuild/.git] / source4 / torture / smbtorture.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB torture tester
4    Copyright (C) Andrew Tridgell 1997-2003
5    Copyright (C) Jelmer Vernooij 2006
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 #include "lib/cmdline/popt_common.h"
24 #include "system/time.h"
25 #include "system/wait.h"
26 #include "system/filesys.h"
27 #include "system/readline.h"
28 #include "lib/smbreadline/smbreadline.h"
29 #include "libcli/libcli.h"
30 #include "lib/ldb/include/ldb.h"
31 #include "lib/events/events.h"
32 #include "dynconfig.h"
33
34 #include "torture/torture.h"
35 #include "build.h"
36 #include "lib/util/dlinklist.h"
37 #include "librpc/rpc/dcerpc.h"
38
39 static bool run_matching(struct torture_context *torture,
40                                                  const char *prefix, 
41                                                  const char *expr,
42                                                  struct torture_suite *suite,
43                                                  bool *matched)
44 {
45         bool ret = true;
46
47         if (suite == NULL) {
48                 struct torture_suite *o;
49
50                 for (o = torture_root->children; o; o = o->next) {
51                         if (gen_fnmatch(expr, o->name) == 0) {
52                                 *matched = true;
53                                 init_iconv();
54                                 ret &= torture_run_suite(torture, o);
55                                 continue;
56                         }
57
58                         ret &= run_matching(torture, o->name, expr, o, matched);
59                 }
60         } else {
61                 char *name;
62                 struct torture_suite *c;
63                 struct torture_tcase *t;
64
65                 for (c = suite->children; c; c = c->next) {
66                         asprintf(&name, "%s-%s", prefix, c->name);
67                         if (gen_fnmatch(expr, name) == 0) {
68                                 *matched = true;
69                                 init_iconv();
70                                 ret &= torture_run_suite(torture, c);
71                                 free(name);
72                                 continue;
73                         }
74                         
75                         ret &= run_matching(torture, name, expr, c, matched);
76
77                         free(name);
78                 }
79
80                 for (t = suite->testcases; t; t = t->next) {
81                         asprintf(&name, "%s-%s", prefix, t->name);
82                         if (gen_fnmatch(expr, name) == 0) {
83                                 *matched = true;
84                                 init_iconv();
85                                 ret &= torture_run_tcase(torture, t);
86                         }
87                         free(name);
88                 }
89         }
90
91         return ret;
92 }
93
94 #define MAX_COLS 80 /* FIXME: Determine this at run-time */
95
96 /****************************************************************************
97 run a specified test or "ALL"
98 ****************************************************************************/
99 static bool run_test(struct torture_context *torture, const char *name)
100 {
101         bool ret = true;
102         bool matched = false;
103         struct torture_suite *o;
104
105         if (strequal(name, "ALL")) {
106                 for (o = torture_root->children; o; o = o->next) {
107                         ret &= torture_run_suite(torture, o);
108                 }
109                 return ret;
110         }
111
112         ret = run_matching(torture, NULL, name, NULL, &matched);
113
114         if (!matched) {
115                 printf("Unknown torture operation '%s'\n", name);
116                 return false;
117         }
118
119         return ret;
120 }
121
122 static void parse_dns(const char *dns)
123 {
124         char *userdn, *basedn, *secret;
125         char *p, *d;
126
127         /* retrievieng the userdn */
128         p = strchr_m(dns, '#');
129         if (!p) {
130                 lp_set_cmdline("torture:ldap_userdn", "");
131                 lp_set_cmdline("torture:ldap_basedn", "");
132                 lp_set_cmdline("torture:ldap_secret", "");
133                 return;
134         }
135         userdn = strndup(dns, p - dns);
136         lp_set_cmdline("torture:ldap_userdn", userdn);
137
138         /* retrieve the basedn */
139         d = p + 1;
140         p = strchr_m(d, '#');
141         if (!p) {
142                 lp_set_cmdline("torture:ldap_basedn", "");
143                 lp_set_cmdline("torture:ldap_secret", "");
144                 return;
145         }
146         basedn = strndup(d, p - d);
147         lp_set_cmdline("torture:ldap_basedn", basedn);
148
149         /* retrieve the secret */
150         p = p + 1;
151         if (!p) {
152                 lp_set_cmdline("torture:ldap_secret", "");
153                 return;
154         }
155         secret = strdup(p);
156         lp_set_cmdline("torture:ldap_secret", secret);
157
158         printf ("%s - %s - %s\n", userdn, basedn, secret);
159
160 }
161
162 static void usage(poptContext pc)
163 {
164         struct torture_suite *o;
165         struct torture_suite *s;
166         struct torture_tcase *t;
167         int i;
168
169         poptPrintUsage(pc, stdout, 0);
170         printf("\n");
171
172         printf("The binding format is:\n\n");
173
174         printf("  TRANSPORT:host[flags]\n\n");
175
176         printf("  where TRANSPORT is either ncacn_np for SMB, ncacn_ip_tcp for RPC/TCP\n");
177         printf("  or ncalrpc for local connections.\n\n");
178
179         printf("  'host' is an IP or hostname or netbios name. If the binding string\n");
180         printf("  identifies the server side of an endpoint, 'host' may be an empty\n");
181         printf("  string.\n\n");
182
183         printf("  'flags' can include a SMB pipe name if using the ncacn_np transport or\n");
184         printf("  a TCP port number if using the ncacn_ip_tcp transport, otherwise they\n");
185         printf("  will be auto-determined.\n\n");
186
187         printf("  other recognised flags are:\n\n");
188
189         printf("    sign : enable ntlmssp signing\n");
190         printf("    seal : enable ntlmssp sealing\n");
191         printf("    connect : enable rpc connect level auth (auth, but no sign or seal)\n");
192         printf("    validate: enable the NDR validator\n");
193         printf("    print: enable debugging of the packets\n");
194         printf("    bigendian: use bigendian RPC\n");
195         printf("    padcheck: check reply data for non-zero pad bytes\n\n");
196
197         printf("  For example, these all connect to the samr pipe:\n\n");
198
199         printf("    ncacn_np:myserver\n");
200         printf("    ncacn_np:myserver[samr]\n");
201         printf("    ncacn_np:myserver[\\pipe\\samr]\n");
202         printf("    ncacn_np:myserver[/pipe/samr]\n");
203         printf("    ncacn_np:myserver[samr,sign,print]\n");
204         printf("    ncacn_np:myserver[\\pipe\\samr,sign,seal,bigendian]\n");
205         printf("    ncacn_np:myserver[/pipe/samr,seal,validate]\n");
206         printf("    ncacn_np:\n");
207         printf("    ncacn_np:[/pipe/samr]\n\n");
208
209         printf("    ncacn_ip_tcp:myserver\n");
210         printf("    ncacn_ip_tcp:myserver[1024]\n");
211         printf("    ncacn_ip_tcp:myserver[1024,sign,seal]\n\n");
212
213         printf("    ncalrpc:\n\n");
214
215         printf("The UNC format is:\n\n");
216
217         printf("  //server/share\n\n");
218
219         printf("Tests are:");
220
221         for (o = torture_root->children; o; o = o->next) {
222                 printf("\n%s (%s):\n  ", o->description, o->name);
223
224                 i = 0;
225                 for (s = o->children; s; s = s->next) {
226                         if (i + strlen(o->name) + strlen(s->name) >= (MAX_COLS - 3)) {
227                                 printf("\n  ");
228                                 i = 0;
229                         }
230                         i+=printf("%s-%s ", o->name, s->name);
231                 }
232
233                 for (t = o->testcases; t; t = t->next) {
234                         if (i + strlen(o->name) + strlen(t->name) >= (MAX_COLS - 3)) {
235                                 printf("\n  ");
236                                 i = 0;
237                         }
238                         i+=printf("%s-%s ", o->name, t->name);
239                 }
240
241                 if (i) printf("\n");
242         }
243
244         printf("\nThe default test is ALL.\n");
245
246         exit(1);
247 }
248
249 static bool is_binding_string(const char *binding_string)
250 {
251         TALLOC_CTX *mem_ctx = talloc_named_const(NULL, 0, "is_binding_string");
252         struct dcerpc_binding *binding_struct;
253         NTSTATUS status;
254         
255         status = dcerpc_parse_binding(mem_ctx, binding_string, &binding_struct);
256
257         talloc_free(mem_ctx);
258         return NT_STATUS_IS_OK(status);
259 }
260
261 static void max_runtime_handler(int sig)
262 {
263         DEBUG(0,("maximum runtime exceeded for smbtorture - terminating\n"));
264         exit(1);
265 }
266
267 struct timeval last_suite_started;
268
269 static void simple_suite_start(struct torture_context *ctx,
270                                                            struct torture_suite *suite)
271 {
272         last_suite_started = timeval_current();
273         printf("Running %s\n", suite->name);
274 }
275
276 static void simple_suite_finish(struct torture_context *ctx,
277                                                            struct torture_suite *suite)
278 {
279
280         printf("%s took %g secs\n\n", suite->name, 
281                    timeval_elapsed(&last_suite_started));
282 }
283
284 static void simple_test_result (struct torture_context *context, 
285                                                                 enum torture_result res, const char *reason)
286 {
287         switch (res) {
288         case TORTURE_OK:
289                 if (reason)
290                         printf("OK: %s\n", reason);
291                 break;
292         case TORTURE_FAIL:
293                 printf("TEST %s FAILED! - %s\n", context->active_test->name, reason);
294                 break;
295         case TORTURE_ERROR:
296                 printf("ERROR IN TEST %s! - %s\n", context->active_test->name, reason); 
297                 break;
298         case TORTURE_SKIP:
299                 printf("SKIP: %s - %s\n", context->active_test->name, reason);
300                 break;
301         }
302 }
303
304 static void simple_comment (struct torture_context *test, 
305                                                         const char *comment)
306 {
307         printf("%s", comment);
308 }
309
310 const static struct torture_ui_ops std_ui_ops = {
311         .comment = simple_comment,
312         .suite_start = simple_suite_start,
313         .suite_finish = simple_suite_finish,
314         .test_result = simple_test_result
315 };
316
317
318 static void subunit_test_start (struct torture_context *ctx, 
319                                                             struct torture_tcase *tcase,
320                                                                 struct torture_test *test)
321 {
322         printf("test: %s\n", test->name);
323 }
324
325 static void subunit_test_result (struct torture_context *context, 
326                                                                  enum torture_result res, const char *reason)
327 {
328         switch (res) {
329         case TORTURE_OK:
330                 printf("success: %s", context->active_test->name);
331                 break;
332         case TORTURE_FAIL:
333                 printf("failure: %s", context->active_test->name);
334                 break;
335         case TORTURE_ERROR:
336                 printf("error: %s", context->active_test->name);
337                 break;
338         case TORTURE_SKIP:
339                 printf("skip: %s", context->active_test->name);
340                 break;
341         }
342         if (reason)
343                 printf(" [ %s ]", reason);
344         printf("\n");
345 }
346
347 static void subunit_comment (struct torture_context *test, 
348                                                          const char *comment)
349 {
350         fprintf(stderr, "%s", comment);
351 }
352
353 const static struct torture_ui_ops subunit_ui_ops = {
354         .comment = subunit_comment,
355         .test_start = subunit_test_start,
356         .test_result = subunit_test_result
357 };
358
359 static void harness_test_start (struct torture_context *ctx, 
360                                                             struct torture_tcase *tcase,
361                                                                 struct torture_test *test)
362 {
363 }
364
365 static void harness_test_result (struct torture_context *context, 
366                                                                  enum torture_result res, const char *reason)
367 {
368         switch (res) {
369         case TORTURE_OK:
370                 printf("ok %s - %s\n", context->active_test->name, reason);
371                 break;
372         case TORTURE_FAIL:
373         case TORTURE_ERROR:
374                 printf("not ok %s - %s\n", context->active_test->name, reason);
375                 break;
376         case TORTURE_SKIP:
377                 printf("skip %s - %s\n", context->active_test->name, reason);
378                 break;
379         }
380 }
381
382 static void harness_comment (struct torture_context *test, 
383                                                          const char *comment)
384 {
385         printf("# %s\n", comment);
386 }
387
388 const static struct torture_ui_ops harness_ui_ops = {
389         .comment = harness_comment,
390         .test_start = harness_test_start,
391         .test_result = harness_test_result
392 };
393
394 static void quiet_suite_start(struct torture_context *ctx,
395                                                   struct torture_suite *suite)
396 {
397         int i;
398         ctx->quiet = true;
399         for (i = 1; i < ctx->level; i++) putchar('\t');
400         printf("%s: ", suite->name);
401         fflush(stdout);
402 }
403
404 static void quiet_suite_finish(struct torture_context *ctx,
405                                                   struct torture_suite *suite)
406 {
407         putchar('\n');
408 }
409
410 static void quiet_test_result (struct torture_context *context, 
411                                                            enum torture_result res, const char *reason)
412 {
413         fflush(stdout);
414         switch (res) {
415         case TORTURE_OK: putchar('.'); break;
416         case TORTURE_FAIL: putchar('F'); break;
417         case TORTURE_ERROR: putchar('E'); break;
418         case TORTURE_SKIP: putchar('I'); break;
419         }
420 }
421
422 const static struct torture_ui_ops quiet_ui_ops = {
423         .suite_start = quiet_suite_start,
424         .suite_finish = quiet_suite_finish,
425         .test_result = quiet_test_result
426 };
427
428 void run_shell(struct torture_context *tctx)
429 {
430         char *cline;
431         int argc;
432         char **argv;
433         int ret;
434
435         while (1) {
436                 cline = smb_readline("torture> ", NULL, NULL);
437
438                 if (cline == NULL)
439                         return;
440         
441                 ret = poptParseArgvString(cline, &argc, &argv);
442                 if (ret != 0) {
443                         fprintf(stderr, "Error parsing line\n");
444                         continue;
445                 }
446
447                 if (!strcmp(argv[0], "quit")) {
448                         return;
449                 } else if (!strcmp(argv[0], "set")) {
450                         if (argc < 3) {
451                                 fprintf(stderr, "Usage: set <variable> <value>\n");
452                         } else {
453                                 char *name = talloc_asprintf(NULL, "torture:%s", argv[1]);
454                                 lp_set_cmdline(name, argv[2]);
455                                 talloc_free(name);
456                         }
457                 } else if (!strcmp(argv[0], "help")) {
458                         fprintf(stderr, "Available commands:\n"
459                                                         " help - This help command\n"
460                                                         " run - Run test\n"
461                                                         " set - Change variables\n"
462                                                         "\n");
463                 } else if (!strcmp(argv[0], "run")) {
464                         if (argc < 2) {
465                                 fprintf(stderr, "Usage: run TEST-NAME [OPTIONS...]\n");
466                         } else {
467                                 run_test(tctx, argv[1]);
468                         }
469                 }
470         }
471 }
472
473 /****************************************************************************
474   main program
475 ****************************************************************************/
476 int main(int argc,char *argv[])
477 {
478         int opt, i;
479         bool correct = true;
480         int max_runtime=0;
481         int argc_new;
482         struct torture_context *torture;
483         const struct torture_ui_ops *ui_ops;
484         char **argv_new;
485         poptContext pc;
486         static const char *target = "other";
487         const char **subunit_dir;
488         int shell = False;
489         static const char *ui_ops_name = "simple";
490         enum {OPT_LOADFILE=1000,OPT_UNCLIST,OPT_TIMELIMIT,OPT_DNS,
491               OPT_DANGEROUS,OPT_SMB_PORTS,OPT_ASYNC};
492         
493         struct poptOption long_options[] = {
494                 POPT_AUTOHELP
495                 {"format", 0, POPT_ARG_STRING, &ui_ops_name, 0, "Output format (one of: simple, subunit, harness)", NULL },
496                 {"smb-ports",   'p', POPT_ARG_STRING, NULL,     OPT_SMB_PORTS,  "SMB ports",    NULL},
497                 {"seed",          0, POPT_ARG_INT,  &torture_seed,      0,      "seed",         NULL},
498                 {"num-ops",       0, POPT_ARG_INT,  &torture_numops,    0,      "num ops",      NULL},
499                 {"entries",       0, POPT_ARG_INT,  &torture_entries,   0,      "entries",      NULL},
500                 {"show-all",      0, POPT_ARG_NONE, &torture_showall,   0,      "show all",     NULL},
501                 {"loadfile",      0, POPT_ARG_STRING,   NULL,   OPT_LOADFILE,   "loadfile",     NULL},
502                 {"unclist",       0, POPT_ARG_STRING,   NULL,   OPT_UNCLIST,    "unclist",      NULL},
503                 {"timelimit",   't', POPT_ARG_STRING,   NULL,   OPT_TIMELIMIT,  "timelimit",    NULL},
504                 {"failures",    'f', POPT_ARG_INT,  &torture_failures,  0,      "failures",     NULL},
505                 {"parse-dns",   'D', POPT_ARG_STRING,   NULL,   OPT_DNS,        "parse-dns",    NULL},
506                 {"dangerous",   'X', POPT_ARG_NONE,     NULL,   OPT_DANGEROUS,
507                  "run dangerous tests (eg. wiping out password database)", NULL},
508                 {"shell",               's', POPT_ARG_NONE, &shell, True, "Run shell", NULL},
509                 {"target",              'T', POPT_ARG_STRING, &target, 0, "samba3|samba4|other", NULL},
510                 {"async",       'a', POPT_ARG_NONE,     NULL,   OPT_ASYNC,
511                  "run async tests", NULL},
512                 {"num-async",    0, POPT_ARG_INT,  &torture_numasync,  0,
513                  "number of simultaneous async requests", NULL},
514                 {"maximum-runtime", 0, POPT_ARG_INT, &max_runtime, 0, 
515                  "set maximum time for smbtorture to live", "seconds"},
516                 POPT_COMMON_SAMBA
517                 POPT_COMMON_CONNECTION
518                 POPT_COMMON_CREDENTIALS
519                 POPT_COMMON_VERSION
520                 { NULL }
521         };
522
523         setlinebuf(stdout);
524
525         /* we are never interested in SIGPIPE */
526         BlockSignals(true,SIGPIPE);
527
528         pc = poptGetContext("smbtorture", argc, (const char **) argv, long_options, 
529                             POPT_CONTEXT_KEEP_FIRST);
530
531         poptSetOtherOptionHelp(pc, "<binding>|<unc> TEST1 TEST2 ...");
532
533         while((opt = poptGetNextOpt(pc)) != -1) {
534                 switch (opt) {
535                 case OPT_LOADFILE:
536                         lp_set_cmdline("torture:loadfile", poptGetOptArg(pc));
537                         break;
538                 case OPT_UNCLIST:
539                         lp_set_cmdline("torture:unclist", poptGetOptArg(pc));
540                         break;
541                 case OPT_TIMELIMIT:
542                         lp_set_cmdline("torture:timelimit", poptGetOptArg(pc));
543                         break;
544                 case OPT_DNS:
545                         parse_dns(poptGetOptArg(pc));
546                         break;
547                 case OPT_DANGEROUS:
548                         lp_set_cmdline("torture:dangerous", "Yes");
549                         break;
550                 case OPT_ASYNC:
551                         lp_set_cmdline("torture:async", "Yes");
552                         break;
553                 case OPT_SMB_PORTS:
554                         lp_set_cmdline("smb ports", poptGetOptArg(pc));
555                         break;
556                 }
557         }
558
559         if (strcmp(target, "samba3") == 0) {
560                 lp_set_cmdline("torture:samba3", "true");
561                 lp_set_cmdline("torture:knownfail", "samba3-knownfail");
562         } else if (strcmp(target, "samba4") == 0) {
563                 lp_set_cmdline("torture:samba4", "true");
564                 lp_set_cmdline("torture:knownfail", "samba4-knownfail");
565         }
566
567         if (max_runtime) {
568                 /* this will only work if nobody else uses alarm(),
569                    which means it won't work for some tests, but we
570                    can't use the event context method we use for smbd
571                    as so many tests create their own event
572                    context. This will at least catch most cases. */
573                 signal(SIGALRM, max_runtime_handler);
574                 alarm(max_runtime);
575         }
576
577         torture_init();
578         ldb_global_init();
579
580         subunit_dir = lp_parm_string_list(-1, "torture", "subunitdir", ":");
581         if (subunit_dir == NULL) 
582                 torture_subunit_load_testsuites(dyn_TORTUREDIR, true, NULL);
583         else {
584                 for (i = 0; subunit_dir[i]; i++)
585                         torture_subunit_load_testsuites(subunit_dir[i], true, NULL);
586         }
587
588         if (torture_seed == 0) {
589                 torture_seed = time(NULL);
590         } 
591         printf("Using seed %d\n", torture_seed);
592         srandom(torture_seed);
593
594         argv_new = discard_const_p(char *, poptGetArgs(pc));
595
596         argc_new = argc;
597         for (i=0; i<argc; i++) {
598                 if (argv_new[i] == NULL) {
599                         argc_new = i;
600                         break;
601                 }
602         }
603
604         if (!(argc_new >= 3 || (shell && argc_new >= 2))) {
605                 usage(pc);
606                 exit(1);
607         }
608
609         /* see if its a RPC transport specifier */
610         if (is_binding_string(argv_new[1])) {
611                 lp_set_cmdline("torture:binding", argv_new[1]);
612         } else {
613                 char *binding = NULL;
614                 char *host = NULL, *share = NULL;
615
616                 if (!smbcli_parse_unc(argv_new[1], NULL, &host, &share)) {
617                         d_printf("Invalid option: %s is not a valid torture target (share or binding string)\n\n", argv_new[1]);
618                         usage(pc);
619                 }
620
621                 lp_set_cmdline("torture:host", host);
622                 lp_set_cmdline("torture:share", share);
623                 asprintf(&binding, "ncacn_np:%s", host);
624                 lp_set_cmdline("torture:binding", binding);
625         }
626
627         if (!strcmp(ui_ops_name, "simple")) {
628                 ui_ops = &std_ui_ops;
629         } else if (!strcmp(ui_ops_name, "subunit")) {
630                 ui_ops = &subunit_ui_ops;
631         } else if (!strcmp(ui_ops_name, "harness")) {
632                 ui_ops = &harness_ui_ops;
633         } else if (!strcmp(ui_ops_name, "quiet")) {
634                 ui_ops = &quiet_ui_ops;
635         } else {
636                 printf("Unknown output format '%s'\n", ui_ops_name);
637                 exit(1);
638         }
639
640         torture = torture_context_init(talloc_autofree_context(), 
641                                 lp_parm_string(-1, "torture", "knownfail"), ui_ops);
642
643         if (argc_new == 0) {
644                 printf("You must specify a test to run, or 'ALL'\n");
645         } else if (shell) {
646                 run_shell(torture);
647         } else {
648                 int total;
649                 double rate;
650                 int unexpected_failures;
651                 for (i=2;i<argc_new;i++) {
652                         if (!run_test(torture, argv_new[i])) {
653                                 correct = false;
654                         }
655                 }
656
657                 unexpected_failures = str_list_length(torture->results.unexpected_failures);
658
659                 total = torture->results.skipped+torture->results.success+torture->results.failed+torture->results.errors;
660                 if (total == 0) {
661                         printf("No tests run.\n");
662                 } else {
663                         rate = ((total - unexpected_failures - torture->results.errors) * (100.0 / total));
664                 
665                         printf("Tests: %d, Failures: %d", total, torture->results.failed);
666                         if (torture->results.failed - unexpected_failures) {
667                                 printf(" (%d expected)", torture->results.failed - unexpected_failures);
668                         }
669                         printf(", Errors: %d, Skipped: %d. Success rate: %.2f%%\n",
670                            torture->results.errors, torture->results.skipped, rate);
671                 }
672
673                 if (unexpected_failures) {
674                         printf("The following tests failed:\n");
675                         for (i = 0; torture->results.unexpected_failures[i]; i++) {
676                                 printf("  %s\n", torture->results.unexpected_failures[i]);
677                         }
678                         printf("\n");
679                 }
680
681                 if (str_list_length(torture->results.unexpected_errors)) {
682                         printf("Errors occurred while running the following tests:\n");
683                         for (i = 0; torture->results.unexpected_errors[i]; i++) {
684                                 printf("  %s\n", torture->results.unexpected_errors[i]);
685                         }
686                         printf("\n");
687                 }
688
689                 if (str_list_length(torture->results.unexpected_successes)) {
690                         printf("The following tests were expected to fail but succeeded:\n");
691                         for (i = 0; torture->results.unexpected_successes[i]; i++) {
692                                 printf("  %s\n", torture->results.unexpected_successes[i]);
693                         }
694                         printf("\n");
695                 }
696         }
697
698         if (torture->results.returncode) {
699                 return(0);
700         } else {
701                 return(1);
702         }
703 }