summaryrefslogtreecommitdiff
path: root/test/test_properties.c
diff options
context:
space:
mode:
authorJohn Hawthorn <john.hawthorn@gmail.com>2017-04-03 19:03:47 -0700
committerJohn Hawthorn <john.hawthorn@gmail.com>2017-04-03 19:30:07 -0700
commita47b34dc0797af82947e4608f5b8690aff62ec60 (patch)
treef6594aada4b417cd7934a5d97ceaa51fd9fa3bd7 /test/test_properties.c
parentdfe82eb18d19f7cde3c0f0099718af06c94c314c (diff)
Add property based testing using thief
Diffstat (limited to 'test/test_properties.c')
-rw-r--r--test/test_properties.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/test/test_properties.c b/test/test_properties.c
index e69de29..1fcdf80 100644
--- a/test/test_properties.c
+++ b/test/test_properties.c
@@ -0,0 +1,163 @@
+#define _DEFAULT_SOURCE
+#include <string.h>
+
+#include "greatest/greatest.h"
+#include "theft/theft.h"
+
+#include "match.h"
+
+static void *string_alloc_cb(struct theft *t, theft_hash seed, void *env) {
+ (void)env;
+ int limit = 128;
+
+ size_t sz = (size_t)(seed % limit) + 1;
+ char *str = malloc(sz + 1);
+ if (str == NULL) {
+ return THEFT_ERROR;
+ }
+
+ for (size_t i = 0; i < sz; i += sizeof(theft_hash)) {
+ theft_hash s = theft_random(t);
+ for (uint8_t b = 0; b < sizeof(theft_hash); b++) {
+ if (i + b >= sz) {
+ break;
+ }
+ str[i + b] = (uint8_t)(s >> (8 * b)) & 0xff;
+ }
+ }
+ str[sz] = 0;
+
+ return str;
+}
+
+static void string_free_cb(void *instance, void *env) {
+ free(instance);
+ (void)env;
+}
+
+static void string_print_cb(FILE *f, void *instance, void *env) {
+ char *str = (char *)instance;
+ (void)env;
+ size_t size = strlen(str);
+ fprintf(f, "str[%zd]:\n ", size);
+ uint8_t bytes = 0;
+ for (size_t i = 0; i < size; i++) {
+ fprintf(f, "%02x", str[i]);
+ bytes++;
+ if (bytes == 16) {
+ fprintf(f, "\n ");
+ bytes = 0;
+ }
+ }
+ fprintf(f, "\n");
+}
+
+static uint64_t string_hash_cb(void *instance, void *env) {
+ char *str = (char *)instance;
+ int size = strlen(str);
+ (void)env;
+ return theft_hash_onepass(str, size);
+}
+
+static void *string_shrink_cb(void *instance, uint32_t tactic, void *env) {
+ char *str = (char *)instance;
+ int n = strlen(str);
+
+ if (tactic == 0) { /* first half */
+ return strndup(str, n / 2);
+ } else if (tactic == 1) { /* second half */
+ return strndup(str + (n / 2), n / 2);
+ } else {
+ return THEFT_NO_MORE_TACTICS;
+ }
+}
+
+static struct theft_type_info string_info = {
+ .alloc = string_alloc_cb,
+ .free = string_free_cb,
+ .print = string_print_cb,
+ .hash = string_hash_cb,
+ .shrink = string_shrink_cb,
+};
+
+static theft_trial_res prop_should_return_results_if_there_is_a_match(char *needle,
+ char *haystack) {
+ int match_exists = has_match(needle, haystack);
+ if (!match_exists)
+ return THEFT_TRIAL_SKIP;
+
+ score_t score = match(needle, haystack);
+
+ if (needle[0] == '\0')
+ return THEFT_TRIAL_SKIP;
+
+ if (score == SCORE_MIN)
+ return THEFT_TRIAL_FAIL;
+
+ return THEFT_TRIAL_PASS;
+}
+
+TEST should_return_results_if_there_is_a_match() {
+ struct theft *t = theft_init(0);
+ struct theft_cfg cfg = {
+ .name = __func__,
+ .fun = prop_should_return_results_if_there_is_a_match,
+ .type_info = {&string_info, &string_info},
+ .trials = 100000,
+ };
+
+ theft_run_res res = theft_run(t, &cfg);
+ theft_free(t);
+ GREATEST_ASSERT_EQm("should_return_results_if_there_is_a_match", THEFT_RUN_PASS, res);
+ PASS();
+}
+
+static theft_trial_res prop_positions_should_match_characters_in_string(char *needle,
+ char *haystack) {
+ int match_exists = has_match(needle, haystack);
+ if (!match_exists)
+ return THEFT_TRIAL_SKIP;
+
+ int n = strlen(needle);
+ size_t *positions = calloc(n, sizeof(size_t));
+ if (!positions)
+ return THEFT_TRIAL_ERROR;
+
+ match_positions(needle, haystack, positions);
+
+ /* Must be increasing */
+ for (int i = 1; i < n; i++) {
+ if (positions[i] <= positions[i - 1]) {
+ return THEFT_TRIAL_FAIL;
+ }
+ }
+
+ for (int i = 0; i < n; i++) {
+ if (toupper(needle[i]) != toupper(haystack[positions[i]])) {
+ return THEFT_TRIAL_FAIL;
+ }
+ }
+
+ free(positions);
+ return THEFT_TRIAL_PASS;
+}
+
+TEST positions_should_match_characters_in_string() {
+ struct theft *t = theft_init(0);
+ struct theft_cfg cfg = {
+ .name = __func__,
+ .fun = prop_positions_should_match_characters_in_string,
+ .type_info = {&string_info, &string_info},
+ .trials = 100000,
+ };
+
+ theft_run_res res = theft_run(t, &cfg);
+ theft_free(t);
+ GREATEST_ASSERT_EQm("should_return_results_if_there_is_a_match", THEFT_RUN_PASS, res);
+ PASS();
+}
+
+SUITE(properties) {
+ RUN_TEST(should_return_results_if_there_is_a_match);
+ RUN_TEST(positions_should_match_characters_in_string);
+}