#ifndef THEFT_TYPES_H #define THEFT_TYPES_H /* A pseudo-random number/seed, used to generate instances. */ typedef uint64_t theft_seed; /* A hash of an instance. */ typedef uint64_t theft_hash; /* These are opaque, as far as the API is concerned. */ struct theft_bloom; /* bloom filter */ struct theft_mt; /* mersenne twister PRNG */ /* Struct for property-testing state. */ struct theft { FILE *out; theft_seed seed; uint8_t requested_bloom_bits; struct theft_bloom *bloom; /* bloom filter */ struct theft_mt *mt; /* random number generator */ }; /* Special sentinel values returned instead of instance pointers. */ #define THEFT_SKIP ((void *)-1) #define THEFT_ERROR ((void *)-2) #define THEFT_DEAD_END ((void *)-1) #define THEFT_NO_MORE_TACTICS ((void *)-3) /* Explicitly disable using the bloom filter. * Note that if you do this, you must be sure your simplify function * *always* returns a simpler value, or it will loop forever. */ #define THEFT_BLOOM_DISABLE ((uint8_t)-1) /* Allocate and return an instance of the type, based on a known * pseudo-random number seed. To get additional seeds, use * theft_random(t); this stream of numbers will be deterministic, so if * the alloc callback is constructed appropriately, an identical * instance can be constructed later from the same initial seed. * * Returns a pointer to the instance, THEFT_ERROR, or THEFT_SKIP. */ typedef void *(theft_alloc_cb)(struct theft *t, theft_seed seed, void *env); /* Free an instance. */ typedef void (theft_free_cb)(void *instance, void *env); /* Hash an instance. Used to skip combinations of arguments which * have probably already been checked. */ typedef theft_hash (theft_hash_cb)(void *instance, void *env); /* Attempt to shrink an instance to a simpler instance. * * For a given INSTANCE, there are likely to be multiple ways in which * it can be simplified. For example, a list of unsigned ints could have * the first element decremented, divided by 2, or dropped. This * callback should return a pointer to a freshly allocated, simplified * instance, or should return THEFT_DEAD_END to indicate that the * instance cannot be simplified further by this method. * * These tactics will be lazily explored breadth-first, to * try to find simpler versions of arguments that cause the * property to no longer hold. * * If this callback is NULL, it is equivalent to always returning * THEFT_NO_MORE_TACTICS. */ typedef void *(theft_shrink_cb)(void *instance, uint32_t tactic, void *env); /* Print INSTANCE to output stream F. * Used for displaying counter-examples. Can be NULL. */ typedef void (theft_print_cb)(FILE *f, void *instance, void *env); /* Result from a single trial. */ typedef enum { THEFT_TRIAL_PASS, /* property held */ THEFT_TRIAL_FAIL, /* property contradicted */ THEFT_TRIAL_SKIP, /* user requested skip; N/A */ THEFT_TRIAL_DUP, /* args probably already tried */ THEFT_TRIAL_ERROR, /* unrecoverable error, halt */ } theft_trial_res; /* A test property function. Arguments must match the types specified by * theft_cfg.type_info, or the result will be undefined. For example, a * propfun `prop_foo(A x, B y, C z)` must have a type_info array of * `{ info_A, info_B, info_C }`. * * Should return: * THEFT_TRIAL_PASS if the property holds, * THEFT_TRIAL_FAIL if a counter-example is found, * THEFT_TRIAL_SKIP if the combination of args isn't applicable, * or THEFT_TRIAL_ERROR if the whole run should be halted. */ typedef theft_trial_res (theft_propfun)( /* arguments unconstrained */ ); /* Callbacks used for testing with random instances of a type. * For more information, see comments on their typedefs. */ struct theft_type_info { /* Required: */ theft_alloc_cb *alloc; /* gen random instance from seed */ /* Optional, but recommended: */ theft_free_cb *free; /* free instance */ theft_hash_cb *hash; /* instance -> hash */ theft_shrink_cb *shrink; /* shrink instance */ theft_print_cb *print; /* fprintf instance */ }; /* Result from an individual trial. */ struct theft_trial_info { const char *name; /* property name */ int trial; /* N'th trial */ theft_seed seed; /* Seed used */ theft_trial_res status; /* Run status */ uint8_t arity; /* Number of arguments */ void **args; /* Arguments used */ }; /* Whether to keep running trials after N failures/skips/etc. */ typedef enum { THEFT_PROGRESS_CONTINUE, /* keep running trials */ THEFT_PROGRESS_HALT, /* no need to continue */ } theft_progress_callback_res; /* Handle test results. * Can be used to halt after too many failures, print '.' after * every N trials, etc. */ typedef theft_progress_callback_res (theft_progress_cb)(struct theft_trial_info *info, void *env); /* Result from a trial run. */ typedef enum { THEFT_RUN_PASS = 0, /* no failures */ THEFT_RUN_FAIL = 1, /* 1 or more failures */ THEFT_RUN_ERROR = 2, /* an error occurred */ THEFT_RUN_ERROR_BAD_ARGS = -1, /* API misuse */ /* Missing required callback for 1 or more types */ THEFT_RUN_ERROR_MISSING_CALLBACK = -2, } theft_run_res; /* Optional report from a trial run; same meanings as theft_trial_res. */ struct theft_trial_report { size_t pass; size_t fail; size_t skip; size_t dup; }; /* Configuration struct for a theft test. * In C99, this struct can be specified as a literal, like this: * * struct theft_cfg cfg = { * .name = "example", * .fun = prop_fun, * .type_info = { type_arg_a, type_arg_b }, * .seed = 0x7he5eed, * }; * * and omitted fields will be set to defaults. * */ struct theft_cfg { /* Property function under test, and info about its arguments. * The function is called with as many arguments are there * are values in TYPE_INFO, so it can crash if that is wrong. */ theft_propfun *fun; struct theft_type_info *type_info[THEFT_MAX_ARITY]; /* -- All fields after this point are optional. -- */ /* Property name, displayed in test runner output. */ const char *name; /* Array of seeds to always run, and its length. * Can be used for regression tests. */ int always_seed_count; /* number of seeds */ theft_seed *always_seeds; /* seeds to always run */ /* Number of trials to run. Defaults to THEFT_DEF_TRIALS. */ int trials; /* Progress callback, used to display progress in * slow-running tests, halt early under certain conditions, etc. */ theft_progress_cb *progress_cb; /* Environment pointer. This is completely opaque to theft itself, * but will be passed along to all callbacks. */ void *env; /* Struct to populate with more detailed test results. */ struct theft_trial_report *report; /* Seed for the random number generator. */ theft_seed seed; }; /* Internal state for incremental hashing. */ struct theft_hasher { theft_hash accum; }; #endif