Browse code

Add property based testing using thief

John Hawthorn authored on 04/04/2017 02:03:47
Showing 2 changed files

... ...
@@ -321,6 +321,8 @@ TEST test_choices_large_input() {
321 321
 	PASS();
322 322
 }
323 323
 
324
+SUITE(properties);
325
+
324 326
 GREATEST_MAIN_DEFS();
325 327
 
326 328
 int main(int argc, char *argv[]) {
... ...
@@ -345,5 +347,7 @@ int main(int argc, char *argv[]) {
345 347
 	RUN_TEST(test_choices_unicode);
346 348
 	RUN_TEST(test_choices_large_input);
347 349
 
350
+	RUN_SUITE(properties);
351
+
348 352
 	GREATEST_MAIN_END();
349 353
 }
... ...
@@ -0,0 +1,163 @@
1
+#define _DEFAULT_SOURCE
2
+#include <string.h>
3
+
4
+#include "greatest/greatest.h"
5
+#include "theft/theft.h"
6
+
7
+#include "match.h"
8
+
9
+static void *string_alloc_cb(struct theft *t, theft_hash seed, void *env) {
10
+	(void)env;
11
+	int limit = 128;
12
+
13
+	size_t sz = (size_t)(seed % limit) + 1;
14
+	char *str = malloc(sz + 1);
15
+	if (str == NULL) {
16
+		return THEFT_ERROR;
17
+	}
18
+
19
+	for (size_t i = 0; i < sz; i += sizeof(theft_hash)) {
20
+		theft_hash s = theft_random(t);
21
+		for (uint8_t b = 0; b < sizeof(theft_hash); b++) {
22
+			if (i + b >= sz) {
23
+				break;
24
+			}
25
+			str[i + b] = (uint8_t)(s >> (8 * b)) & 0xff;
26
+		}
27
+	}
28
+	str[sz] = 0;
29
+
30
+	return str;
31
+}
32
+
33
+static void string_free_cb(void *instance, void *env) {
34
+	free(instance);
35
+	(void)env;
36
+}
37
+
38
+static void string_print_cb(FILE *f, void *instance, void *env) {
39
+	char *str = (char *)instance;
40
+	(void)env;
41
+	size_t size = strlen(str);
42
+	fprintf(f, "str[%zd]:\n    ", size);
43
+	uint8_t bytes = 0;
44
+	for (size_t i = 0; i < size; i++) {
45
+		fprintf(f, "%02x", str[i]);
46
+		bytes++;
47
+		if (bytes == 16) {
48
+			fprintf(f, "\n    ");
49
+			bytes = 0;
50
+		}
51
+	}
52
+	fprintf(f, "\n");
53
+}
54
+
55
+static uint64_t string_hash_cb(void *instance, void *env) {
56
+	char *str = (char *)instance;
57
+	int size = strlen(str);
58
+	(void)env;
59
+	return theft_hash_onepass(str, size);
60
+}
61
+
62
+static void *string_shrink_cb(void *instance, uint32_t tactic, void *env) {
63
+	char *str = (char *)instance;
64
+	int n = strlen(str);
65
+
66
+	if (tactic == 0) { /* first half */
67
+		return strndup(str, n / 2);
68
+	} else if (tactic == 1) { /* second half */
69
+		return strndup(str + (n / 2), n / 2);
70
+	} else {
71
+		return THEFT_NO_MORE_TACTICS;
72
+	}
73
+}
74
+
75
+static struct theft_type_info string_info = {
76
+    .alloc = string_alloc_cb,
77
+    .free = string_free_cb,
78
+    .print = string_print_cb,
79
+    .hash = string_hash_cb,
80
+    .shrink = string_shrink_cb,
81
+};
82
+
83
+static theft_trial_res prop_should_return_results_if_there_is_a_match(char *needle,
84
+								      char *haystack) {
85
+	int match_exists = has_match(needle, haystack);
86
+	if (!match_exists)
87
+		return THEFT_TRIAL_SKIP;
88
+
89
+	score_t score = match(needle, haystack);
90
+
91
+	if (needle[0] == '\0')
92
+		return THEFT_TRIAL_SKIP;
93
+
94
+	if (score == SCORE_MIN)
95
+		return THEFT_TRIAL_FAIL;
96
+
97
+	return THEFT_TRIAL_PASS;
98
+}
99
+
100
+TEST should_return_results_if_there_is_a_match() {
101
+	struct theft *t = theft_init(0);
102
+	struct theft_cfg cfg = {
103
+	    .name = __func__,
104
+	    .fun = prop_should_return_results_if_there_is_a_match,
105
+	    .type_info = {&string_info, &string_info},
106
+	    .trials = 100000,
107
+	};
108
+
109
+	theft_run_res res = theft_run(t, &cfg);
110
+	theft_free(t);
111
+	GREATEST_ASSERT_EQm("should_return_results_if_there_is_a_match", THEFT_RUN_PASS, res);
112
+	PASS();
113
+}
114
+
115
+static theft_trial_res prop_positions_should_match_characters_in_string(char *needle,
116
+									char *haystack) {
117
+	int match_exists = has_match(needle, haystack);
118
+	if (!match_exists)
119
+		return THEFT_TRIAL_SKIP;
120
+
121
+	int n = strlen(needle);
122
+	size_t *positions = calloc(n, sizeof(size_t));
123
+	if (!positions)
124
+		return THEFT_TRIAL_ERROR;
125
+
126
+	match_positions(needle, haystack, positions);
127
+
128
+	/* Must be increasing */
129
+	for (int i = 1; i < n; i++) {
130
+		if (positions[i] <= positions[i - 1]) {
131
+			return THEFT_TRIAL_FAIL;
132
+		}
133
+	}
134
+
135
+	for (int i = 0; i < n; i++) {
136
+		if (toupper(needle[i]) != toupper(haystack[positions[i]])) {
137
+			return THEFT_TRIAL_FAIL;
138
+		}
139
+	}
140
+
141
+	free(positions);
142
+	return THEFT_TRIAL_PASS;
143
+}
144
+
145
+TEST positions_should_match_characters_in_string() {
146
+	struct theft *t = theft_init(0);
147
+	struct theft_cfg cfg = {
148
+	    .name = __func__,
149
+	    .fun = prop_positions_should_match_characters_in_string,
150
+	    .type_info = {&string_info, &string_info},
151
+	    .trials = 100000,
152
+	};
153
+
154
+	theft_run_res res = theft_run(t, &cfg);
155
+	theft_free(t);
156
+	GREATEST_ASSERT_EQm("should_return_results_if_there_is_a_match", THEFT_RUN_PASS, res);
157
+	PASS();
158
+}
159
+
160
+SUITE(properties) {
161
+	RUN_TEST(should_return_results_if_there_is_a_match);
162
+	RUN_TEST(positions_should_match_characters_in_string);
163
+}