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