-
Notifications
You must be signed in to change notification settings - Fork 5
/
test_suite.h
226 lines (191 loc) · 8.25 KB
/
test_suite.h
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/* Test suite for Hammer.
* Copyright (C) 2012 Meredith L. Patterson, Dan "TQ" Hirsch
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef HAMMER_TEST_SUITE__H
#define HAMMER_TEST_SUITE__H
#include <stdint.h>
#include <stdlib.h>
#include <inttypes.h>
// Equivalent to g_assert_*, but not using g_assert...
#define g_check_inttype(fmt, typ, n1, op, n2) do { \
typ _n1 = (n1); \
typ _n2 = (n2); \
if (!(_n1 op _n2)) { \
g_test_message("Check failed: (%s): (" fmt " %s " fmt ")", \
#n1 " " #op " " #n2, \
_n1, #op, _n2); \
g_test_fail(); \
} \
} while(0)
#define g_check_bytes(len, n1, op, n2) do { \
const uint8_t *_n1 = (n1); \
const uint8_t *_n2 = (n2); \
if (!(memcmp(_n1, _n2, len) op 0)) { \
g_test_message("Check failed: (%s)", \
#n1 " " #op " " #n2); \
g_test_fail(); \
} \
} while(0)
#define g_check_string(n1, op, n2) do { \
const char *_n1 = (n1); \
const char *_n2 = (n2); \
if (!(strcmp(_n1, _n2) op 0)) { \
g_test_message("Check failed: (%s) (%s %s %s)", \
#n1 " " #op " " #n2, \
_n1, #op, _n2); \
g_test_fail(); \
} \
} while(0)
#define g_check_regular(lang) do { \
if (!lang->isValidRegular(lang->env)) { \
g_test_message("Language is not regular"); \
g_test_fail(); \
} \
} while(0)
#define g_check_contextfree(lang) do { \
if (!lang->isValidCF(lang->env)) { \
g_test_message("Language is not context-free"); \
g_test_fail(); \
} \
} while(0)
#define g_check_compilable(lang, backend, params) do { \
if (!h_compile(lang, backend, params)) { \
g_test_message("Language is not %s(%s)", #backend, params); \
g_test_fail(); \
} \
} while(0)
// TODO: replace uses of this with g_check_parse_failed
#define g_check_failed(res) do { \
const HParseResult *result = (res); \
if (NULL != result) { \
g_test_message("Check failed: shouldn't have succeeded, but did"); \
g_test_fail(); \
} \
} while(0)
#define g_check_parse_failed(parser, backend, input, inp_len) do { \
int skip = h_compile((HParser *)(parser), (HParserBackend)backend, NULL); \
if(skip != 0) { \
g_test_message("Backend not applicable, skipping test"); \
break; \
} \
const HParseResult *result = h_parse(parser, (const uint8_t*)input, inp_len); \
if (NULL != result) { \
g_test_message("Check failed: shouldn't have succeeded, but did"); \
g_test_fail(); \
} \
} while(0)
#define g_check_parse_ok(parser, backend, input, inp_len) do { \
int skip = h_compile((HParser *)(parser), (HParserBackend) backend, NULL); \
if(skip) { \
g_test_message("Backend not applicable, skipping test"); \
break; \
} \
HParseResult *res = h_parse(parser, (const uint8_t*)input, inp_len); \
if (!res) { \
g_test_message("Parse failed on line %d", __LINE__); \
g_test_fail(); \
} else { \
HArenaStats stats; \
h_allocator_stats(res->arena, &stats); \
g_test_message("Parse used %zd bytes, wasted %zd bytes. " \
"Inefficiency: %5f%%", \
stats.used, stats.wasted, \
stats.wasted * 100. / (stats.used+stats.wasted)); \
h_delete_arena(res->arena); \
} \
} while(0)
#define g_check_parse_match(parser, backend, input, inp_len, result) do { \
int skip = h_compile((HParser *)(parser), (HParserBackend) backend, NULL); \
if(skip) { \
g_test_message("Backend not applicable, skipping test"); \
break; \
} \
HParseResult *res = h_parse(parser, (const uint8_t*)input, inp_len); \
if (!res) { \
g_test_message("Parse failed on line %d", __LINE__); \
g_test_fail(); \
} else { \
char* cres = h_write_result_unamb(res->ast); \
g_check_string(cres, ==, result); \
free(cres); \
HArenaStats stats; \
h_allocator_stats(res->arena, &stats); \
g_test_message("Parse used %zd bytes, wasted %zd bytes. " \
"Inefficiency: %5f%%", \
stats.used, stats.wasted, \
stats.wasted * 100. / (stats.used+stats.wasted)); \
h_delete_arena(res->arena); \
} \
} while(0)
#define g_check_hashtable_present(table, key) do { \
if(!h_hashtable_present(table, key)) { \
g_test_message("Check failed: key should have been in table, but wasn't"); \
g_test_fail(); \
} \
} while(0)
#define g_check_hashtable_absent(table, key) do { \
if(h_hashtable_present(table, key)) { \
g_test_message("Check failed: key shouldn't have been in table, but was"); \
g_test_fail(); \
} \
} while(0)
#define g_check_hashtable_size(table, n) do { \
size_t expected = n; \
size_t actual = (table)->used; \
if(actual != expected) { \
g_test_message("Check failed: table size should have been %zu, but was %zu", \
expected, actual); \
g_test_fail(); \
} \
} while(0)
#define g_check_stringmap_present(table, key) do { \
bool end = (key[strlen(key)-1] == '$'); \
if(!h_stringmap_present(table, (uint8_t *)key, strlen(key), end)) { \
g_test_message("Check failed: \"%s\" should have been in map, but wasn't", key); \
g_test_fail(); \
} \
} while(0)
#define g_check_stringmap_absent(table, key) do { \
bool end = (key[strlen(key)-1] == '$'); \
if(h_stringmap_present(table, (uint8_t *)key, strlen(key), end)) { \
g_test_message("Check failed: \"%s\" shouldn't have been in map, but was", key); \
g_test_fail(); \
} \
} while(0)
// This stuff needs to be made internal-only; it has no use in user-level test suites
#define g_check_terminal(grammar, parser) \
g_check_hashtable_absent(grammar->nts, h_desugar(&system_allocator, NULL, parser))
#define g_check_nonterminal(grammar, parser) \
g_check_hashtable_present(grammar->nts, h_desugar(&system_allocator, NULL, parser))
#define g_check_derives_epsilon(grammar, parser) \
g_check_hashtable_present(grammar->geneps, h_desugar(&system_allocator, NULL, parser))
#define g_check_derives_epsilon_not(grammar, parser) \
g_check_hashtable_absent(grammar->geneps, h_desugar(&system_allocator, NULL, parser))
#define g_check_firstset_present(k, grammar, parser, str) \
g_check_stringmap_present(h_first(k, grammar, h_desugar(&system_allocator, NULL, parser)), str)
#define g_check_firstset_absent(k, grammar, parser, str) \
g_check_stringmap_absent(h_first(k, grammar, h_desugar(&system_allocator, NULL, parser)), str)
#define g_check_followset_present(k, grammar, parser, str) \
g_check_stringmap_present(h_follow(k, grammar, h_desugar(&system_allocator, NULL, parser)), str)
#define g_check_followset_absent(k, grammar, parser, str) \
g_check_stringmap_absent(h_follow(k, grammar, h_desugar(&system_allocator, NULL, parser)), str)
#define g_check_cmp_int32(n1, op, n2) g_check_inttype("%d", int32_t, n1, op, n2)
#define g_check_cmp_int64(n1, op, n2) g_check_inttype("%" PRId64, int64_t, n1, op, n2)
#define g_check_cmp_uint32(n1, op, n2) g_check_inttype("%u", uint32_t, n1, op, n2)
#define g_check_cmp_uint64(n1, op, n2) g_check_inttype("%" PRIu64, uint64_t, n1, op, n2)
#define g_check_cmpfloat(n1, op, n2) g_check_inttype("%g", float, n1, op, n2)
#define g_check_cmpdouble(n1, op, n2) g_check_inttype("%g", double, n1, op, n2)
#endif // #ifndef HAMMER_TEST_SUITE__H