summaryrefslogtreecommitdiff
path: root/test/test_properties.c
blob: 91982e33401a7c34d4579cdff4ed7dbcee006070 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#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) {
	(void)env;
	char *str = (char *)instance;
	int size = strlen(str);
	return theft_hash_onepass((uint8_t *)str, size);
}

static void *string_shrink_cb(void *instance, uint32_t tactic, void *env) {
	(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;
		}
	}

	/* Matching characters must be in returned positions */
	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_suite) {
	RUN_TEST(should_return_results_if_there_is_a_match);
	RUN_TEST(positions_should_match_characters_in_string);
}