| File: | .build-ci/../tests/unit/c/test-argconfig-diff.c |
| Warning: | line 430, column 11 The 1st argument to 'fdopen' is < 0 but should be >= 0 |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | |||
| 2 | /* | |||
| 3 | * Compare argconfig_parse() and argconfig_parse_global() with a frozen copy | |||
| 4 | * of the getopt based parser (argconfig-getopt-ref.c) on generated command | |||
| 5 | * lines: the return value, all option values, the seen flags and the | |||
| 6 | * operands must be identical. | |||
| 7 | * | |||
| 8 | * Two kinds of input are deliberately not supported anymore and are | |||
| 9 | * filtered out: single-dash long options ("-flag", which getopt_long_only() | |||
| 10 | * accepted) and abbreviated long options ("--fla"). They are tested in | |||
| 11 | * test-argconfig-parse. | |||
| 12 | * | |||
| 13 | * getopt's permutation and error handling differ between C libraries, the | |||
| 14 | * reference is only meaningful with glibc. | |||
| 15 | */ | |||
| 16 | ||||
| 17 | #include <errno(*__errno_location ()).h> | |||
| 18 | #include <locale.h> | |||
| 19 | #include <stdbool.h> | |||
| 20 | #include <stdint.h> | |||
| 21 | #include <stdio.h> | |||
| 22 | #include <stdlib.h> | |||
| 23 | #include <string.h> | |||
| 24 | #include <unistd.h> | |||
| 25 | #include <time.h> | |||
| 26 | ||||
| 27 | #include <ccan/array_size/array_size.h> | |||
| 28 | #include <shared/fs-util.h> | |||
| 29 | ||||
| 30 | #include "../../../src/argconfig.h" | |||
| 31 | #include "argconfig-getopt-ref.h" | |||
| 32 | ||||
| 33 | const char *libnvme_strerror(int errnum); | |||
| 34 | ||||
| 35 | #ifdef __GLIBC__2 | |||
| 36 | ||||
| 37 | struct diff_cfg { | |||
| 38 | bool_Bool flag; | |||
| 39 | uint64_t suffix; | |||
| 40 | uint32_t uint; | |||
| 41 | int int_val; | |||
| 42 | uint64_t long_val; | |||
| 43 | double double_val; | |||
| 44 | uint8_t byte; | |||
| 45 | uint16_t shrt; | |||
| 46 | int incr; | |||
| 47 | char *string; | |||
| 48 | char *str; | |||
| 49 | char *optstr; | |||
| 50 | uint32_t longonly; | |||
| 51 | bool_Bool dup1; | |||
| 52 | bool_Bool dup2; | |||
| 53 | bool_Bool noval; | |||
| 54 | }; | |||
| 55 | ||||
| 56 | struct global_cfg { | |||
| 57 | int verbose; | |||
| 58 | bool_Bool dry_run; | |||
| 59 | char *format; | |||
| 60 | uint32_t timeout; | |||
| 61 | bool_Bool help; | |||
| 62 | }; | |||
| 63 | ||||
| 64 | /* The report; stdout gets the help argconfig prints */ | |||
| 65 | static FILE *report; | |||
| 66 | ||||
| 67 | #define MAX_OPTS32 32 | |||
| 68 | #define MAX_ARGS8 8 | |||
| 69 | ||||
| 70 | struct result { | |||
| 71 | int ret; | |||
| 72 | struct diff_cfg cfg; | |||
| 73 | struct global_cfg gcfg; | |||
| 74 | bool_Bool seen[MAX_OPTS32]; | |||
| 75 | char *operands[MAX_ARGS8]; | |||
| 76 | int nr_operands; | |||
| 77 | int argind; | |||
| 78 | }; | |||
| 79 | ||||
| 80 | static struct argconfig_opt_val byte_vals[] = { | |||
| 81 | VAL_BYTE("one", 1){"one", CFG_BYTE, .val.byte = 1}, | |||
| 82 | VAL_BYTE("two", 2){"two", CFG_BYTE, .val.byte = 2}, | |||
| 83 | VAL_BYTE("three", 3){"three", CFG_BYTE, .val.byte = 3}, | |||
| 84 | VAL_END(){ ((void*)0) } | |||
| 85 | }; | |||
| 86 | ||||
| 87 | static const char * const long_names[] = { | |||
| 88 | "flag", "suffix", "uint", "int", "long", "double", "byte", "shrt", | |||
| 89 | "incr", "string", "str", "optstr", "noval", "longonly", "dup1", | |||
| 90 | "dup2", "help", "verbose", "dry-run", "format", "timeout", | |||
| 91 | }; | |||
| 92 | ||||
| 93 | static const char * const vocabulary[] = { | |||
| 94 | "--flag", "-f", "--flag=1", | |||
| 95 | "--suffix=4Ki", "--suffix", "-s", "-s1", "4Ki", | |||
| 96 | "--uint=5", "--uint", "-u", "-u7", "--uint=", "5", | |||
| 97 | "--int=-3", "-i", "-3", | |||
| 98 | "--long=0x10", "--double=1.5", | |||
| 99 | "--byte=two", "--byte=tw", "--byte=t", "--byte=300", "-b", "one", | |||
| 100 | "--shrt=65535", "--shrt=65536", | |||
| 101 | "--incr", "-I", "-II", "-fI", "-fu9", "-fu", | |||
| 102 | "--string=x", "--string", "-t", "-tval", "--str=y", "--str", | |||
| 103 | "--optstr", "--optstr=z", "-O", "-Oval", | |||
| 104 | "--noval", "-n", "--longonly=4", "--dup1", "--dup2", "-D", | |||
| 105 | "--", "-", "dev", "", "=x", | |||
| 106 | "--bogus", "-z", "--help", "-h", "-?", "-fh", | |||
| 107 | "-v", "-vv", "--verbose", "--dry-run", "-o", "--format=json", | |||
| 108 | "--timeout", "--timeout=10", "list", | |||
| 109 | }; | |||
| 110 | ||||
| 111 | static void diff_opts(struct argconfig_commandline_options *opts, | |||
| 112 | struct diff_cfg *cfg) | |||
| 113 | { | |||
| 114 | struct argconfig_commandline_options o[] = { | |||
| 115 | OPT_FLAG("flag", 'f', &cfg->flag, "flag"){"flag", 'f', ((void*)0), CFG_FLAG, _Generic(&cfg->flag , _Bool * : (&cfg->flag)), ARGCONFIG_ARG_NONE, "flag", 0, }, | |||
| 116 | OPT_SUFFIX("suffix", 's', &cfg->suffix, "suffix"){"suffix", 's', "IONUM", CFG_LONG_SUFFIX, _Generic(&cfg-> suffix, uint64_t * : (&cfg->suffix), default : _Generic (&cfg->suffix, unsigned long long * : (&cfg->suffix ), default : (void)0)), ARGCONFIG_ARG_REQUIRED, "suffix", 0, }, | |||
| 117 | OPT_UINT("uint", 'u', &cfg->uint, "uint"){"uint", 'u', "NUM", CFG_POSITIVE, _Generic(&cfg->uint , uint32_t * : (&cfg->uint)), ARGCONFIG_ARG_REQUIRED, "uint" , 0, }, | |||
| 118 | OPT_INT("int", 'i', &cfg->int_val, "int"){"int", 'i', "NUM", CFG_INT, _Generic(&cfg->int_val, int * : (&cfg->int_val)), ARGCONFIG_ARG_REQUIRED, "int", 0 , }, | |||
| 119 | OPT_LONG("long", 'l', &cfg->long_val, "long"){"long", 'l', "NUM", CFG_LONG, _Generic(&cfg->long_val , uint64_t * : (&cfg->long_val), default : _Generic(& cfg->long_val, unsigned long long * : (&cfg->long_val ), default : (void)0)), ARGCONFIG_ARG_REQUIRED, "long", 0, }, | |||
| 120 | OPT_DOUBLE("double", 'd', &cfg->double_val, "double"){"double", 'd', "NUM", CFG_DOUBLE, _Generic(&cfg->double_val , double * : (&cfg->double_val)), ARGCONFIG_ARG_REQUIRED , "double", 0, }, | |||
| 121 | OPT_BYTE("byte", 'b', &cfg->byte, "byte", byte_vals){"byte", 'b', "NUM", CFG_BYTE, _Generic(&cfg->byte, uint8_t * : (&cfg->byte)), ARGCONFIG_ARG_REQUIRED, "byte", 0, byte_vals}, | |||
| 122 | OPT_GROUP("More options"){"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), ARGCONFIG_ARG_NONE , "More options", 0, ((void*)0)}, | |||
| 123 | OPT_SHRT("shrt", 'S', &cfg->shrt, "shrt"){"shrt", 'S', "NUM", CFG_SHORT, _Generic(&cfg->shrt, uint16_t * : (&cfg->shrt)), ARGCONFIG_ARG_REQUIRED, "shrt", 0, }, | |||
| 124 | OPT_INCR("incr", 'I', &cfg->incr, "incr"){"incr", 'I', "NUM", CFG_INCREMENT, _Generic(&cfg->incr , int * : (&cfg->incr)), ARGCONFIG_ARG_NONE, "incr", 0 , }, | |||
| 125 | OPT_STRING("string", 't', "STRING", &cfg->string, "string"){"string", 't', "STRING", CFG_STRING, _Generic(&cfg->string , char ** : (&cfg->string), const char ** : (&cfg-> string)), ARGCONFIG_ARG_REQUIRED, "string", 0, }, | |||
| 126 | OPT_STR("str", 'r', &cfg->str, "str"){"str", 'r', "STRING", CFG_STRING, _Generic(&cfg->str, char ** : (&cfg->str), const char ** : (&cfg-> str)), ARGCONFIG_ARG_REQUIRED, "str", 0, }, | |||
| 127 | OPT_STRING_OPTIONAL("optstr", 'O', "STR", &cfg->optstr,{"optstr", 'O', "STR", CFG_STRING, _Generic(&cfg->optstr , char ** : (&cfg->optstr), const char ** : (&cfg-> optstr)), ARGCONFIG_ARG_OPTIONAL, "optstr", 0, } | |||
| 128 | "optstr"){"optstr", 'O', "STR", CFG_STRING, _Generic(&cfg->optstr , char ** : (&cfg->optstr), const char ** : (&cfg-> optstr)), ARGCONFIG_ARG_OPTIONAL, "optstr", 0, }, | |||
| 129 | OPT_FLAG("noval", 'n', &cfg->noval, "noval"){"noval", 'n', ((void*)0), CFG_FLAG, _Generic(&cfg->noval , _Bool * : (&cfg->noval)), ARGCONFIG_ARG_NONE, "noval" , 0, }, | |||
| 130 | OPT_UINT("longonly", 0, &cfg->longonly, "longonly"){"longonly", 0, "NUM", CFG_POSITIVE, _Generic(&cfg->longonly , uint32_t * : (&cfg->longonly)), ARGCONFIG_ARG_REQUIRED , "longonly", 0, }, | |||
| 131 | OPT_FLAG("dup1", 'D', &cfg->dup1, "dup1"){"dup1", 'D', ((void*)0), CFG_FLAG, _Generic(&cfg->dup1 , _Bool * : (&cfg->dup1)), ARGCONFIG_ARG_NONE, "dup1", 0, }, | |||
| 132 | OPT_FLAG("dup2", 'D', &cfg->dup2, "dup2"){"dup2", 'D', ((void*)0), CFG_FLAG, _Generic(&cfg->dup2 , _Bool * : (&cfg->dup2)), ARGCONFIG_ARG_NONE, "dup2", 0, }, | |||
| 133 | OPT_END(){ ((void*)0) } | |||
| 134 | }; | |||
| 135 | ||||
| 136 | memcpy(opts, o, sizeof(o)); | |||
| 137 | } | |||
| 138 | ||||
| 139 | static void global_opts(struct argconfig_commandline_options *opts, | |||
| 140 | struct global_cfg *cfg) | |||
| 141 | { | |||
| 142 | struct argconfig_commandline_options o[] = { | |||
| 143 | OPT_INCR("verbose", 'v', &cfg->verbose, "verbose"){"verbose", 'v', "NUM", CFG_INCREMENT, _Generic(&cfg-> verbose, int * : (&cfg->verbose)), ARGCONFIG_ARG_NONE, "verbose", 0, }, | |||
| 144 | OPT_FLAG("dry-run", 0, &cfg->dry_run, "dry-run"){"dry-run", 0, ((void*)0), CFG_FLAG, _Generic(&cfg->dry_run , _Bool * : (&cfg->dry_run)), ARGCONFIG_ARG_NONE, "dry-run" , 0, }, | |||
| 145 | OPT_FMT("format", 'o', &cfg->format, "format"){"format", 'o', "FMT", CFG_STRING, _Generic(&cfg->format , char ** : (&cfg->format), const char ** : (&cfg-> format)), ARGCONFIG_ARG_REQUIRED, "format", 0, }, | |||
| 146 | OPT_UINT("timeout", 0, &cfg->timeout, "timeout"){"timeout", 0, "NUM", CFG_POSITIVE, _Generic(&cfg->timeout , uint32_t * : (&cfg->timeout)), ARGCONFIG_ARG_REQUIRED , "timeout", 0, }, | |||
| 147 | OPT_FLAG("help", 'h', &cfg->help, "help"){"help", 'h', ((void*)0), CFG_FLAG, _Generic(&cfg->help , _Bool * : (&cfg->help)), ARGCONFIG_ARG_NONE, "help", 0, }, | |||
| 148 | OPT_END(){ ((void*)0) } | |||
| 149 | }; | |||
| 150 | ||||
| 151 | memcpy(opts, o, sizeof(o)); | |||
| 152 | } | |||
| 153 | ||||
| 154 | /* The short options of diff_opts() and the implicit help options */ | |||
| 155 | static const char command_shorts[] = "fsuildbSItrnODh?"; | |||
| 156 | ||||
| 157 | /* | |||
| 158 | * True if getopt would treat the argument as a single-dash long option or | |||
| 159 | * as an abbreviated long option, which are not supported anymore. For the | |||
| 160 | * command options getopt_long_only() also took "-x" for an abbreviated | |||
| 161 | * long option if x is not a short option. | |||
| 162 | */ | |||
| 163 | static bool_Bool unsupported(const char *arg, bool_Bool global) | |||
| 164 | { | |||
| 165 | const char *name, *eq; | |||
| 166 | size_t len; | |||
| 167 | unsigned int i; | |||
| 168 | ||||
| 169 | if (arg[0] != '-' || !arg[1]) | |||
| 170 | return false0; | |||
| 171 | if (arg[1] == '-') { | |||
| 172 | name = arg + 2; | |||
| 173 | if (!*name) | |||
| 174 | return false0; | |||
| 175 | } else { | |||
| 176 | name = arg + 1; | |||
| 177 | if (global) | |||
| 178 | return false0; | |||
| 179 | if (!name[1] && strchr(command_shorts, name[0])_Generic (0 ? (command_shorts) : (void *) 1, const void *: (const char *) (strchr (command_shorts, name[0])), default: strchr ( command_shorts, name[0]))) | |||
| 180 | return false0; | |||
| 181 | } | |||
| 182 | ||||
| 183 | eq = strchr(name, '=')_Generic (0 ? (name) : (void *) 1, const void *: (const char * ) (strchr (name, '=')), default: strchr (name, '=')); | |||
| 184 | len = eq ? (size_t)(eq - name) : strlen(name); | |||
| 185 | ||||
| 186 | /* "--name" is fine even if a longer name starts with it */ | |||
| 187 | if (arg[1] == '-') { | |||
| 188 | for (i = 0; i < ARRAY_SIZE(long_names)(sizeof(long_names) / sizeof((long_names)[0]) + 0); i++) { | |||
| 189 | if (strlen(long_names[i]) == len && | |||
| 190 | !strncmp(long_names[i], name, len)) | |||
| 191 | return false0; | |||
| 192 | } | |||
| 193 | } | |||
| 194 | ||||
| 195 | /* "-name", "-nam" and "--nam" are not */ | |||
| 196 | for (i = 0; i < ARRAY_SIZE(long_names)(sizeof(long_names) / sizeof((long_names)[0]) + 0); i++) { | |||
| 197 | if (!strncmp(long_names[i], name, len)) | |||
| 198 | return true1; | |||
| 199 | } | |||
| 200 | ||||
| 201 | return false0; | |||
| 202 | } | |||
| 203 | ||||
| 204 | /* The filter must not skip the supported forms of the comparison */ | |||
| 205 | static bool_Bool filter_test(void) | |||
| 206 | { | |||
| 207 | static const struct { | |||
| 208 | const char *arg; | |||
| 209 | bool_Bool global; | |||
| 210 | bool_Bool unsupported; | |||
| 211 | } tests[] = { | |||
| 212 | /* exact names, also when a longer name starts with them */ | |||
| 213 | { "--str", false0, false0 }, | |||
| 214 | { "--str=y", false0, false0 }, | |||
| 215 | { "--long=0x10", false0, false0 }, | |||
| 216 | { "--string=x", false0, false0 }, | |||
| 217 | { "-f", false0, false0 }, | |||
| 218 | { "-fu9", false0, false0 }, | |||
| 219 | { "dev", false0, false0 }, | |||
| 220 | { "-verbose", true1, false0 }, | |||
| 221 | /* abbreviations and single-dash long options */ | |||
| 222 | { "--stri", false0, true1 }, | |||
| 223 | { "--lon", false0, true1 }, | |||
| 224 | { "--verb", true1, true1 }, | |||
| 225 | { "-str", false0, true1 }, | |||
| 226 | { "-o", false0, true1 }, | |||
| 227 | { "-flag", false0, true1 }, | |||
| 228 | }; | |||
| 229 | bool_Bool ok = true1; | |||
| 230 | unsigned int i; | |||
| 231 | ||||
| 232 | for (i = 0; i < ARRAY_SIZE(tests)(sizeof(tests) / sizeof((tests)[0]) + 0); i++) { | |||
| 233 | if (unsupported(tests[i].arg, tests[i].global) == | |||
| 234 | tests[i].unsupported) | |||
| 235 | continue; | |||
| 236 | fprintf(report, "ERROR: filter: '%s' %s taken as unsupported\n", | |||
| 237 | tests[i].arg, tests[i].unsupported ? "not" : "wrongly"); | |||
| 238 | ok = false0; | |||
| 239 | } | |||
| 240 | ||||
| 241 | return ok; | |||
| 242 | } | |||
| 243 | ||||
| 244 | static void collect_seen(struct argconfig_commandline_options *opts, | |||
| 245 | bool_Bool *seen) | |||
| 246 | { | |||
| 247 | int i; | |||
| 248 | ||||
| 249 | for (i = 0; opts[i].option && i < MAX_OPTS32; i++) | |||
| 250 | seen[i] = opts[i].seen; | |||
| 251 | } | |||
| 252 | ||||
| 253 | static void run(char **args, int nargs, bool_Bool ref, bool_Bool global, | |||
| 254 | struct result *r) | |||
| 255 | { | |||
| 256 | struct argconfig_commandline_options opts[MAX_OPTS32]; | |||
| 257 | char *argv[MAX_ARGS8 + 2]; | |||
| 258 | char **operands; | |||
| 259 | int i; | |||
| 260 | ||||
| 261 | memset(r, 0, sizeof(*r)); | |||
| 262 | argv[0] = "test-argconfig-diff"; | |||
| 263 | for (i = 0; i < nargs; i++) | |||
| 264 | argv[i + 1] = args[i]; | |||
| 265 | argv[nargs + 1] = NULL((void*)0); | |||
| 266 | ||||
| 267 | if (global) { | |||
| 268 | global_opts(opts, &r->gcfg); | |||
| 269 | if (ref) | |||
| 270 | r->ret = ref_argconfig_parse_global(nargs + 1, argv, | |||
| 271 | opts, &r->argind); | |||
| 272 | else | |||
| 273 | r->ret = argconfig_parse_global(nargs + 1, argv, opts, | |||
| 274 | &r->argind); | |||
| 275 | if (r->ret) | |||
| 276 | r->argind = 0; | |||
| 277 | collect_seen(opts, r->seen); | |||
| 278 | return; | |||
| 279 | } | |||
| 280 | ||||
| 281 | diff_opts(opts, &r->cfg); | |||
| 282 | if (ref) { | |||
| 283 | r->ret = ref_argconfig_parse(nargs + 1, argv, opts, | |||
| 284 | &operands, &r->nr_operands); | |||
| 285 | for (i = 0; !r->ret && i < r->nr_operands; i++) | |||
| 286 | r->operands[i] = operands[i]; | |||
| 287 | } else { | |||
| 288 | r->ret = argconfig_parse(nargs + 1, argv, "diff", opts); | |||
| 289 | /* the getopt parser reported the help as an error */ | |||
| 290 | if (r->ret == ARGCONFIG_HELP1) | |||
| 291 | r->ret = -EINVAL22; | |||
| 292 | r->nr_operands = argconfig_nr_operands(); | |||
| 293 | for (i = 0; !r->ret && i < r->nr_operands; i++) | |||
| 294 | r->operands[i] = argconfig_operand(i); | |||
| 295 | } | |||
| 296 | if (r->ret) | |||
| 297 | r->nr_operands = 0; | |||
| 298 | collect_seen(opts, r->seen); | |||
| 299 | } | |||
| 300 | ||||
| 301 | static unsigned long nr_cases, nr_skipped; | |||
| 302 | ||||
| 303 | static bool_Bool compare(char **args, int nargs, bool_Bool global) | |||
| 304 | { | |||
| 305 | struct result ref, cur; | |||
| 306 | int i; | |||
| 307 | ||||
| 308 | for (i = 0; i < nargs; i++) { | |||
| 309 | if (unsupported(args[i], global)) { | |||
| 310 | nr_skipped++; | |||
| 311 | return true1; | |||
| 312 | } | |||
| 313 | } | |||
| 314 | ||||
| 315 | nr_cases++; | |||
| 316 | run(args, nargs, true1, global, &ref); | |||
| 317 | run(args, nargs, false0, global, &cur); | |||
| 318 | ||||
| 319 | if (!memcmp(&ref, &cur, sizeof(ref))) | |||
| 320 | return true1; | |||
| 321 | ||||
| 322 | fprintf(report, "ERROR: %s parse differs for:", | |||
| 323 | global ? "global" : "command"); | |||
| 324 | for (i = 0; i < nargs; i++) | |||
| 325 | fprintf(report, " '%s'", args[i]); | |||
| 326 | fprintf(report, "\n ret %d/%d argind %d/%d operands %d/%d\n", | |||
| 327 | ref.ret, cur.ret, ref.argind, cur.argind, | |||
| 328 | ref.nr_operands, cur.nr_operands); | |||
| 329 | ||||
| 330 | return false0; | |||
| 331 | } | |||
| 332 | ||||
| 333 | /* | |||
| 334 | * Time budgets in seconds. The tests run in an emulator for cross builds | |||
| 335 | * and under valgrind, so the larger parts are limited by time, not by a | |||
| 336 | * number of cases. | |||
| 337 | */ | |||
| 338 | #define BUDGET_THREE_WORDS2.0 2.0 | |||
| 339 | #define BUDGET_RANDOM1.0 1.0 | |||
| 340 | ||||
| 341 | static double now(void) | |||
| 342 | { | |||
| 343 | struct timespec ts; | |||
| 344 | ||||
| 345 | clock_gettime(CLOCK_MONOTONIC1, &ts); | |||
| 346 | return ts.tv_sec + ts.tv_nsec / 1e9; | |||
| 347 | } | |||
| 348 | ||||
| 349 | static bool_Bool expired(double end, unsigned long i) | |||
| 350 | { | |||
| 351 | /* reading the clock for every case would dominate the runtime */ | |||
| 352 | return !(i % 256) && now() > end; | |||
| 353 | } | |||
| 354 | ||||
| 355 | /* All sequences of up to two words from the vocabulary */ | |||
| 356 | static bool_Bool two_words(bool_Bool global) | |||
| 357 | { | |||
| 358 | unsigned int n = ARRAY_SIZE(vocabulary)(sizeof(vocabulary) / sizeof((vocabulary)[0]) + 0), i, j; | |||
| 359 | char *args[2]; | |||
| 360 | bool_Bool ok = compare(args, 0, global); | |||
| 361 | ||||
| 362 | for (i = 0; i < n; i++) { | |||
| 363 | args[0] = (char *)vocabulary[i]; | |||
| 364 | ok &= compare(args, 1, global); | |||
| 365 | for (j = 0; j < n; j++) { | |||
| 366 | args[1] = (char *)vocabulary[j]; | |||
| 367 | ok &= compare(args, 2, global); | |||
| 368 | } | |||
| 369 | } | |||
| 370 | ||||
| 371 | return ok; | |||
| 372 | } | |||
| 373 | ||||
| 374 | /* Sequences of three words, in a fixed order until the time is up */ | |||
| 375 | static bool_Bool three_words(bool_Bool global, unsigned long *count) | |||
| 376 | { | |||
| 377 | unsigned int n = ARRAY_SIZE(vocabulary)(sizeof(vocabulary) / sizeof((vocabulary)[0]) + 0), i; | |||
| 378 | double end = now() + BUDGET_THREE_WORDS2.0; | |||
| 379 | char *args[3]; | |||
| 380 | bool_Bool ok = true1; | |||
| 381 | ||||
| 382 | for (i = 0; i < n * n * n && !expired(end, i); i++) { | |||
| 383 | args[0] = (char *)vocabulary[i / (n * n)]; | |||
| 384 | args[1] = (char *)vocabulary[i / n % n]; | |||
| 385 | args[2] = (char *)vocabulary[i % n]; | |||
| 386 | ok &= compare(args, 3, global); | |||
| 387 | } | |||
| 388 | *count += i; | |||
| 389 | ||||
| 390 | return ok; | |||
| 391 | } | |||
| 392 | ||||
| 393 | /* | |||
| 394 | * Longer sequences until the time is up. The seed is fixed and a failure | |||
| 395 | * prints its arguments, so it can be reproduced. | |||
| 396 | */ | |||
| 397 | static bool_Bool random_cases(bool_Bool global, unsigned long *count) | |||
| 398 | { | |||
| 399 | unsigned int n = ARRAY_SIZE(vocabulary)(sizeof(vocabulary) / sizeof((vocabulary)[0]) + 0); | |||
| 400 | double end = now() + BUDGET_RANDOM1.0; | |||
| 401 | unsigned int seed = 0x6e766d65; | |||
| 402 | char *args[MAX_ARGS8]; | |||
| 403 | unsigned long i; | |||
| 404 | bool_Bool ok = true1; | |||
| 405 | int nargs, j; | |||
| 406 | ||||
| 407 | for (i = 0; !expired(end, i); i++) { | |||
| 408 | seed = seed * 1103515245 + 12345; | |||
| 409 | nargs = 4 + (seed >> 16) % (MAX_ARGS8 - 3); | |||
| 410 | for (j = 0; j < nargs; j++) { | |||
| 411 | seed = seed * 1103515245 + 12345; | |||
| 412 | args[j] = (char *)vocabulary[(seed >> 16) % n]; | |||
| 413 | } | |||
| 414 | ok &= compare(args, nargs, global); | |||
| 415 | } | |||
| 416 | *count += i; | |||
| 417 | ||||
| 418 | return ok; | |||
| 419 | } | |||
| 420 | ||||
| 421 | int main(void) | |||
| 422 | { | |||
| 423 | unsigned int n = ARRAY_SIZE(vocabulary)(sizeof(vocabulary) / sizeof((vocabulary)[0]) + 0); | |||
| 424 | unsigned long nr_three = 0, nr_random = 0; | |||
| 425 | bool_Bool ok = true1; | |||
| 426 | FILE *f; | |||
| 427 | ||||
| 428 | setlocale(LC_NUMERIC1, "C"); | |||
| 429 | unsetenv("POSIXLY_CORRECT"); | |||
| 430 | report = fdopen(dup(STDOUT_FILENO1), "w"); | |||
| ||||
| ||||
| 431 | if (!report || !freopen(shr_dev_null(), "w", stdoutstdout)) { | |||
| 432 | printf("ERROR: redirecting stdout failed: %s\n", | |||
| 433 | libnvme_strerror(errno(*__errno_location ()))); | |||
| 434 | return EXIT_FAILURE1; | |||
| 435 | } | |||
| 436 | f = freopen(shr_dev_null(), "w", stderrstderr); | |||
| 437 | if (!f) { | |||
| 438 | fprintf(report, "ERROR: reopening stderr failed: %s\n", | |||
| 439 | libnvme_strerror(errno(*__errno_location ()))); | |||
| 440 | return EXIT_FAILURE1; | |||
| 441 | } | |||
| 442 | ||||
| 443 | ok &= filter_test(); | |||
| 444 | ok &= two_words(false0); | |||
| 445 | ok &= two_words(true1); | |||
| 446 | ok &= three_words(false0, &nr_three); | |||
| 447 | ok &= three_words(true1, &nr_three); | |||
| 448 | ok &= random_cases(false0, &nr_random); | |||
| 449 | ok &= random_cases(true1, &nr_random); | |||
| 450 | ||||
| 451 | fprintf(report, "%lu cases compared, %lu skipped\n", nr_cases, | |||
| 452 | nr_skipped); | |||
| 453 | fprintf(report, "three words: %lu of %u, random: %lu\n", | |||
| 454 | nr_three, 2 * n * n * n, nr_random); | |||
| 455 | fclose(f); | |||
| 456 | fclose(report); | |||
| 457 | ||||
| 458 | return ok ? EXIT_SUCCESS0 : EXIT_FAILURE1; | |||
| 459 | } | |||
| 460 | ||||
| 461 | #else /* __GLIBC__ */ | |||
| 462 | ||||
| 463 | int main(void) | |||
| 464 | { | |||
| 465 | return 77; | |||
| 466 | } | |||
| 467 | ||||
| 468 | #endif /* __GLIBC__ */ |