Line data Source code
1 : #include "passgen/parser/token.h"
2 : #include "tests.h"
3 :
4 : #define CODEPOINT_MAX 0x10FFFF
5 :
6 : // Test that normal (non-escaped) tokens are parsed corredly. They should
7 : // just be fed through.
8 1 : test_result test_token_normal(void) {
9 : passgen_token_parser parser;
10 : passgen_token token;
11 1 : passgen_token_parser_init(&parser);
12 :
13 1114112 : for(uint32_t codepoint = 0; codepoint < CODEPOINT_MAX; codepoint++) {
14 1114111 : if(codepoint == '\\') {
15 1 : continue;
16 : }
17 1114110 : int ret = passgen_token_parse(&parser, &token, 1, codepoint);
18 1114110 : assert_eq(ret, PASSGEN_TOKEN_INIT);
19 1114110 : assert_eq(parser.state, PASSGEN_TOKEN_INIT);
20 1114110 : assert_eq(token.codepoint, codepoint);
21 : }
22 :
23 1 : return test_ok;
24 : }
25 :
26 : // Test that regular escapes are handled correctly: they should resolve
27 : // to the appropriate characters.
28 1 : test_result test_token_escaped(void) {
29 : passgen_token_parser parser;
30 : passgen_token token;
31 1 : passgen_token_parser_init(&parser);
32 :
33 1 : uint32_t escapes[][2] = {
34 : {'\\', '\\'},
35 : {'a', '\a'},
36 : {'b', '\b'},
37 : {'f', '\f'},
38 : {'r', '\r'},
39 : {'n', '\n'},
40 : {'t', '\t'},
41 : {'v', '\v'},
42 : {'e', '\033'},
43 : {0, 0}};
44 :
45 10 : for(size_t i = 0; escapes[i][0]; i++) {
46 9 : assert_eq(
47 : passgen_token_parse(&parser, &token, 1, '\\'),
48 : PASSGEN_TOKEN_ESCAPED);
49 9 : assert_eq(parser.state, PASSGEN_TOKEN_ESCAPED);
50 9 : assert_eq(
51 : passgen_token_parse(&parser, &token, 1, escapes[i][0]),
52 : PASSGEN_TOKEN_INIT);
53 9 : assert_eq(parser.state, PASSGEN_TOKEN_INIT);
54 9 : assert_eq(token.codepoint, escapes[i][1]);
55 : }
56 :
57 1 : return test_ok;
58 : }
59 :
60 : // Test that regular characters have the escape bit set when parsed with
61 : // leading backslashes.
62 1 : test_result test_token_special_escaped(void) {
63 : passgen_token_parser parser;
64 : passgen_token token;
65 1 : passgen_token_parser_init(&parser);
66 :
67 1114112 : for(uint32_t codepoint = 0; codepoint < CODEPOINT_MAX; codepoint++) {
68 1114111 : switch(codepoint) {
69 10 : case '\\':
70 : case 'a':
71 : case 'b':
72 : case 'f':
73 : case 'r':
74 : case 'n':
75 : case 't':
76 : case 'v':
77 : case 'u':
78 : case 'e':
79 10 : continue;
80 1114101 : default:
81 1114101 : break;
82 : }
83 1114101 : int ret = passgen_token_parse(&parser, &token, 1, '\\');
84 1114101 : assert_eq(ret, PASSGEN_TOKEN_ESCAPED);
85 1114101 : assert_eq(parser.state, PASSGEN_TOKEN_ESCAPED);
86 :
87 1114101 : ret = passgen_token_parse(&parser, &token, 1, codepoint);
88 1114101 : assert_eq(ret, PASSGEN_TOKEN_INIT);
89 1114101 : assert_eq(parser.state, PASSGEN_TOKEN_INIT);
90 1114101 : assert_eq(token.codepoint, (codepoint | PASSGEN_TOKEN_ESCAPED_BIT));
91 : }
92 :
93 1 : return test_ok;
94 : }
95 :
96 : // Test that escaped unicode tokens (eg: \u{ffff}) get parsed correctly.
97 1 : test_result test_token_unicode(void) {
98 : passgen_token_parser parser;
99 : passgen_token token;
100 1 : passgen_token_parser_init(&parser);
101 :
102 : char buffer[8];
103 1114112 : for(uint32_t codepoint = 0; codepoint < CODEPOINT_MAX; codepoint++) {
104 1114111 : sprintf(buffer, "%x", codepoint);
105 1114111 : int ret = passgen_token_parse(&parser, &token, 1, '\\');
106 1114111 : assert_eq(ret, PASSGEN_TOKEN_ESCAPED);
107 :
108 1114111 : ret = passgen_token_parse(&parser, &token, 1, 'u');
109 1114111 : assert_eq(ret, PASSGEN_TOKEN_UNICODE);
110 :
111 1114111 : ret = passgen_token_parse(&parser, &token, 1, '{');
112 1114111 : assert_eq(ret, PASSGEN_TOKEN_UNICODE_PAYLOAD);
113 1114111 : assert_eq(parser.data.unicode_payload.length, 0);
114 :
115 6680297 : for(size_t i = 0; buffer[i]; i++) {
116 5566186 : ret = passgen_token_parse(&parser, &token, 1, buffer[i]);
117 5566186 : assert_eq(ret, PASSGEN_TOKEN_UNICODE_PAYLOAD);
118 5566186 : assert_eq(parser.data.unicode_payload.length, i + 1);
119 : }
120 :
121 1114111 : ret = passgen_token_parse(&parser, &token, 1, '}');
122 1114111 : assert_eq(ret, PASSGEN_TOKEN_INIT);
123 1114111 : assert_eq(token.codepoint, codepoint);
124 : }
125 :
126 1 : return test_ok;
127 : }
128 :
129 : // Test that passing any character that is not an opening brace after \u
130 : // causes an error state (so \u{FC} is fine, but \u[ is not).
131 1 : test_result test_token_unicode_error_start(void) {
132 : passgen_token_parser parser;
133 : passgen_token token;
134 1 : passgen_token_parser_init(&parser);
135 :
136 1 : uint32_t chars[] =
137 : {'a', 'b', 'c', 'x', 'y', '0', '9', '-', '_', '}', '[', 0};
138 :
139 12 : for(size_t i = 0; chars[i]; i++) {
140 11 : passgen_token_parser_init(&parser);
141 11 : assert_eq(
142 : passgen_token_parse(&parser, &token, 1, '\\'),
143 : PASSGEN_TOKEN_ESCAPED);
144 11 : assert_eq(
145 : passgen_token_parse(&parser, &token, 1, 'u'),
146 : PASSGEN_TOKEN_UNICODE);
147 11 : assert_eq(
148 : passgen_token_parse(&parser, &token, 1, chars[i]),
149 : PASSGEN_TOKEN_ERROR_UNICODE_START);
150 : }
151 :
152 1 : return test_ok;
153 : }
154 :
155 : // Test that passing any character that is not a hexadecimal character after \u{
156 : // causes an error state (so \u{FC} is fine, but \u{ZZ} is not).
157 1 : test_result test_token_unicode_error_payload(void) {
158 : passgen_token_parser parser;
159 : passgen_token token;
160 :
161 1 : uint32_t chars[] = {'x', ' ', '_', '-', '!', '+', '=', 'w', 'g', '[', 0};
162 :
163 11 : for(size_t i = 0; chars[i]; i++) {
164 10 : passgen_token_parser_init(&parser);
165 10 : assert_eq(
166 : passgen_token_parse(&parser, &token, 1, '\\'),
167 : PASSGEN_TOKEN_ESCAPED);
168 10 : assert_eq(
169 : passgen_token_parse(&parser, &token, 1, 'u'),
170 : PASSGEN_TOKEN_UNICODE);
171 10 : assert_eq(
172 : passgen_token_parse(&parser, &token, 1, '{'),
173 : PASSGEN_TOKEN_UNICODE_PAYLOAD);
174 10 : assert_eq(
175 : passgen_token_parse(&parser, &token, 1, chars[i]),
176 : PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD);
177 : }
178 :
179 1 : return test_ok;
180 : }
181 :
182 : // Test that passing any character that is not an opening brace after \u
183 : // causes an error state (so \u{FC} is fine, but \u[ is not).
184 1 : test_result test_token_unicode_error_len(void) {
185 : passgen_token_parser parser;
186 : passgen_token token;
187 :
188 1 : const uint32_t inputs[][7] = {
189 : {'0', '0', '0', '0', '0', '0', '0'},
190 : {'1', '0', 'f', 'f', 'f', 'f', '0'},
191 : {'f', 'f', 'f', 'f', 'f', 'f', 'f'},
192 : {'0', '1', '2', '3', '4', '5', '6'},
193 : {0}};
194 :
195 5 : for(size_t i = 0; inputs[i][0]; i++) {
196 4 : passgen_token_parser_init(&parser);
197 4 : assert_eq(
198 : passgen_token_parse(&parser, &token, 1, '\\'),
199 : PASSGEN_TOKEN_ESCAPED);
200 4 : assert_eq(
201 : passgen_token_parse(&parser, &token, 1, 'u'),
202 : PASSGEN_TOKEN_UNICODE);
203 4 : assert_eq(
204 : passgen_token_parse(&parser, &token, 1, '{'),
205 : PASSGEN_TOKEN_UNICODE_PAYLOAD);
206 4 : assert_eq(parser.data.unicode_payload.length, 0);
207 28 : for(size_t c = 0; c < 6; c++) {
208 24 : assert_eq(
209 : passgen_token_parse(&parser, &token, 1, inputs[i][c]),
210 : PASSGEN_TOKEN_UNICODE_PAYLOAD);
211 24 : assert_eq(parser.data.unicode_payload.length, c + 1);
212 : }
213 4 : assert_eq(
214 : passgen_token_parse(&parser, &token, 1, inputs[i][6]),
215 : PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD_LEN);
216 4 : assert_eq(parser.data.unicode_payload.length, 7);
217 : }
218 :
219 1 : return test_ok;
220 : }
221 :
222 1 : test_result test_token_state_string(void) {
223 : // initial state
224 1 : assert(passgen_token_state_string(PASSGEN_TOKEN_INIT) != NULL);
225 :
226 : // multi-charpoint states
227 1 : assert(passgen_token_state_string(PASSGEN_TOKEN_ESCAPED) != NULL);
228 1 : assert(passgen_token_state_string(PASSGEN_TOKEN_UNICODE) != NULL);
229 1 : assert(passgen_token_state_string(PASSGEN_TOKEN_UNICODE_PAYLOAD) != NULL);
230 :
231 : // error states
232 1 : assert(
233 : passgen_token_state_string(PASSGEN_TOKEN_ERROR_UNICODE_START) != NULL);
234 1 : assert(
235 : passgen_token_state_string(PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD) !=
236 : NULL);
237 1 : assert(
238 : passgen_token_state_string(PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD_LEN) !=
239 : NULL);
240 :
241 : // undefined
242 1 : assert(
243 : passgen_token_state_string(PASSGEN_TOKEN_UNICODE_PAYLOAD + 1) == NULL);
244 1 : assert(
245 : passgen_token_state_string(
246 : PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD_LEN - 1) == NULL);
247 :
248 1 : return test_ok;
249 : }
250 :
251 : // Test that the token parser correctly keeps track of byte and codepoint
252 : // offsets.
253 1 : test_result test_token_normal_offsets(void) {
254 1 : passgen_token_parser parser = {0};
255 1 : passgen_token token = {0};
256 :
257 : #define PARSE(cp, width) \
258 : assert( \
259 : passgen_token_parse(&parser, &token, width, cp) == \
260 : PASSGEN_TOKEN_INIT); \
261 : assert(parser.state == PASSGEN_TOKEN_INIT); \
262 : assert(token.codepoint == cp);
263 :
264 1 : PARSE('a', 1);
265 1 : assert(parser.offset == 1);
266 1 : assert(parser.byte_offset == 1);
267 1 : assert(token.offset == 0);
268 1 : assert(token.byte_offset == 0);
269 :
270 1 : PARSE('b', 1);
271 1 : assert(parser.offset == 2);
272 1 : assert(parser.byte_offset == 2);
273 1 : assert(token.offset == 1);
274 1 : assert(token.byte_offset == 1);
275 :
276 1 : PARSE('c', 2);
277 1 : assert(parser.offset == 3);
278 1 : assert(parser.byte_offset == 4);
279 1 : assert(token.offset == 2);
280 1 : assert(token.byte_offset == 2);
281 :
282 1 : PARSE(' ', 2);
283 1 : assert(parser.offset == 4);
284 1 : assert(parser.byte_offset == 6);
285 1 : assert(token.offset == 3);
286 1 : assert(token.byte_offset == 4);
287 :
288 1 : PARSE('!', 3);
289 1 : assert(parser.offset == 5);
290 1 : assert(parser.byte_offset == 9);
291 1 : assert(token.offset == 4);
292 1 : assert(token.byte_offset == 6);
293 :
294 1 : PARSE('[', 3);
295 1 : assert(parser.offset == 6);
296 1 : assert(parser.byte_offset == 12);
297 1 : assert(token.offset == 5);
298 1 : assert(token.byte_offset == 9);
299 :
300 1 : PARSE(']', 4);
301 1 : assert(parser.offset == 7);
302 1 : assert(parser.byte_offset == 16);
303 1 : assert(token.offset == 6);
304 1 : assert(token.byte_offset == 12);
305 :
306 : #undef PARSE
307 :
308 1 : return test_ok;
309 : }
310 :
311 : // Test that the token parser correctly keeps track of byte and codepoint
312 : // offsets.
313 1 : test_result test_token_multi_offsets(void) {
314 1 : passgen_token_parser parser = {0};
315 1 : passgen_token token = {0};
316 :
317 1 : assert(
318 : passgen_token_parse(&parser, &token, 1, '\\') == PASSGEN_TOKEN_ESCAPED);
319 1 : assert(passgen_token_parse(&parser, &token, 1, '[') == PASSGEN_TOKEN_INIT);
320 1 : assert(parser.offset == 2);
321 1 : assert(parser.byte_offset == 2);
322 1 : assert(token.offset == 0);
323 1 : assert(token.byte_offset == 0);
324 :
325 1 : assert(
326 : passgen_token_parse(&parser, &token, 1, '\\') == PASSGEN_TOKEN_ESCAPED);
327 1 : assert(passgen_token_parse(&parser, &token, 1, ']') == PASSGEN_TOKEN_INIT);
328 1 : assert(parser.offset == 4);
329 1 : assert(parser.byte_offset == 4);
330 1 : assert(token.offset == 2);
331 1 : assert(token.byte_offset == 2);
332 :
333 1 : assert(
334 : passgen_token_parse(&parser, &token, 1, '\\') == PASSGEN_TOKEN_ESCAPED);
335 1 : assert(
336 : passgen_token_parse(&parser, &token, 1, 'u') == PASSGEN_TOKEN_UNICODE);
337 1 : assert(
338 : passgen_token_parse(&parser, &token, 1, '{') ==
339 : PASSGEN_TOKEN_UNICODE_PAYLOAD);
340 1 : assert(
341 : passgen_token_parse(&parser, &token, 1, '0') ==
342 : PASSGEN_TOKEN_UNICODE_PAYLOAD);
343 1 : assert(
344 : passgen_token_parse(&parser, &token, 1, 'a') ==
345 : PASSGEN_TOKEN_UNICODE_PAYLOAD);
346 1 : assert(passgen_token_parse(&parser, &token, 1, '}') == PASSGEN_TOKEN_INIT);
347 1 : assert(parser.offset == 10);
348 1 : assert(parser.byte_offset == 10);
349 1 : assert(token.offset == 4);
350 1 : assert(token.byte_offset == 4);
351 :
352 1 : return test_ok;
353 : }
354 :
355 : // Test that parsing any character in an error state simply returns that error
356 : // state.
357 1 : test_result test_token_error_propagation(void) {
358 : passgen_token_parser parser;
359 : passgen_token token;
360 1 : int errors[] = {
361 : PASSGEN_TOKEN_ERROR_UNICODE_START,
362 : PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD,
363 : PASSGEN_TOKEN_ERROR_UNICODE_PAYLOAD_LEN,
364 : 0};
365 :
366 4 : for(size_t i = 0; errors[i]; i++) {
367 3 : passgen_token_parser_init(&parser);
368 3 : parser.state = errors[i];
369 3 : assert_eq(passgen_token_parse(&parser, &token, 1, 'a'), errors[i]);
370 : }
371 :
372 1 : return test_ok;
373 : }
|