blob: 33481d63b496afa0dce3d924bdcfb76cb4939953 [file] [log] [blame]
Brian Silverman70325d62015-09-20 17:00:43 -04001/* Copyright (c) 2007, Google Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <assert.h>
35
36#include "htmlparser/statemachine.h"
37
38enum {
39 SIMPLE_STATE_A,
40 SIMPLE_STATE_B,
41 SIMPLE_STATE_C,
42 SIMPLE_STATE_D,
43 SIMPLE_STATE_ERROR_TEST
44};
45
46/* Include the test state machine definition. */
47#include "tests/statemachine_test_fsm.h"
48
49/* Taken from google templates */
50
51#define ASSERT(cond) do { \
52 if (!(cond)) { \
53 printf("%s: %d: ASSERT FAILED: %s\n", __FILE__, __LINE__, \
54 #cond); \
55 assert(cond); \
56 exit(1); \
57 } \
58} while (0)
59
60#define ASSERT_STREQ(a, b) do { \
61 if (strcmp((a), (b))) { \
62 printf("%s: %d: ASSERT FAILED: '%s' != '%s'\n", __FILE__, __LINE__, \
63 (a), (b)); \
64 assert(!strcmp((a), (b))); \
65 exit(1); \
66 } \
67} while (0)
68
69#define ASSERT_STRSTR(text, substr) do { \
70 if (!strstr((text), (substr))) { \
71 printf("%s: %d: ASSERT FAILED: '%s' not in '%s'\n", \
72 __FILE__, __LINE__, (substr), (text)); \
73 assert(strstr((text), (substr))); \
74 exit(1); \
75 } \
76} while (0)
77
78
79#define NUM_STATES 10
80
81/* To simply the tests */
82#define statemachine_parse_str(a,b) statemachine_parse(a, b, strlen(b));
83
84/* Simple state machine test. */
85int test_simple()
86{
87 statemachine_definition *def;
88 statemachine_ctx *sm;
89 def = statemachine_definition_new(NUM_STATES);
90 sm = statemachine_new(def, NULL);
91
92 statemachine_definition_populate(def, simple_state_transitions,
93 simple_states_internal_names);
94 ASSERT(sm->current_state == SIMPLE_STATE_A);
95
96 statemachine_parse(sm, "001", 3);
97 ASSERT(sm->current_state == SIMPLE_STATE_B);
98
99 statemachine_parse(sm, "001", 3);
100 ASSERT(sm->current_state == SIMPLE_STATE_C);
101
102 statemachine_parse(sm, "2", 1);
103 ASSERT(sm->current_state == SIMPLE_STATE_B);
104
105 statemachine_parse(sm, "11", 2);
106 ASSERT(sm->current_state == SIMPLE_STATE_D);
107
108 statemachine_delete(sm);
109 return 0;
110}
111
112/* Tests error handling logic when we try to follow non existent transitions. */
113int test_error()
114{
115 statemachine_definition *def;
116 statemachine_ctx *sm;
117 int res;
118
119 def = statemachine_definition_new(NUM_STATES);
120 sm = statemachine_new(def, NULL);
121
122 statemachine_definition_populate(def, simple_state_transitions,
123 NULL);
124 ASSERT(sm->current_state == SIMPLE_STATE_A);
125
126 ASSERT(statemachine_get_error_msg(sm) == NULL);
127
128 res = statemachine_parse_str(sm, "00E");
129 ASSERT(sm->current_state == SIMPLE_STATE_ERROR_TEST);
130 ASSERT(sm->current_state == res);
131
132 res = statemachine_parse(sm, "3", 1);
133 ASSERT(res == STATEMACHINE_ERROR);
134 ASSERT_STREQ(statemachine_get_error_msg(sm),
135 "Unexpected character '3'");
136
137 statemachine_reset(sm);
138 ASSERT(statemachine_get_error_msg(sm) == NULL);
139
140 statemachine_delete(sm);
141
142 def = statemachine_definition_new(NUM_STATES);
143 sm = statemachine_new(def, NULL);
144
145 statemachine_definition_populate(def, simple_state_transitions,
146 simple_states_internal_names);
147 ASSERT(sm->current_state == SIMPLE_STATE_A);
148
149 res = statemachine_parse_str(sm, "00E");
150 ASSERT(sm->current_state == SIMPLE_STATE_ERROR_TEST);
151 ASSERT(sm->current_state == res);
152
153 res = statemachine_parse(sm, "3", 1);
154 ASSERT(res == STATEMACHINE_ERROR);
155 ASSERT_STREQ(statemachine_get_error_msg(sm),
156 "Unexpected character '3' in state 'error_test'");
157
158 statemachine_delete(sm);
159
160 return 0;
161}
162
163/* Tests htmlparser_start_record() and htmlparser_end_record() logic. */
164
165int test_record()
166{
167 statemachine_definition *def;
168 statemachine_ctx *sm;
169 const char *actual;
170 char expected[STATEMACHINE_RECORD_BUFFER_SIZE];
171 int res;
172 int counter;
173 def = statemachine_definition_new(NUM_STATES);
174 sm = statemachine_new(def, NULL);
175
176 statemachine_definition_populate(def, simple_state_transitions,
177 simple_states_internal_names);
178
179 ASSERT(sm->current_state == SIMPLE_STATE_A);
180
181 res = statemachine_parse_str(sm, "001");
182 ASSERT(sm->current_state == SIMPLE_STATE_B);
183 ASSERT(sm->current_state == res);
184
185 statemachine_start_record(sm);
186 statemachine_parse_str(sm, "121212");
187 ASSERT_STREQ("121212", statemachine_stop_record(sm));
188
189 statemachine_parse_str(sm, "not recorded");
190
191 statemachine_start_record(sm);
192 statemachine_parse_str(sm, "121212000");
193 ASSERT_STREQ("121212000", statemachine_stop_record(sm));
194
195 statemachine_start_record(sm);
196 statemachine_parse_str(sm, "1234567890");
197 ASSERT_STREQ("1234567890", statemachine_record_buffer(sm));
198
199 statemachine_parse_str(sm, "test");
200 ASSERT_STREQ("1234567890test", statemachine_stop_record(sm));
201
202 statemachine_start_record(sm);
203
204 /* Record 1000 chars + strlen("beginning-") */
205 statemachine_parse_str(sm, "beginning-");
206 for (counter = 0; counter < 100; counter++) {
207 statemachine_parse_str(sm, "1234567890");
208 }
209
210 /* Make sure we preserved the start of the buffer. */
211 ASSERT_STRSTR(statemachine_record_buffer(sm), "beginning-");
212
213 /* And make sure the size is what we expect. */
214 ASSERT(STATEMACHINE_RECORD_BUFFER_SIZE - 1 ==
215 strlen(statemachine_stop_record(sm)));
216
217 statemachine_start_record(sm);
218 for (counter = 0; counter < 100; counter++) {
219 statemachine_parse_str(sm, "0123456789ABCDEF");
220 }
221
222 expected[0] = '\0';
223 /* Fill the buffer with a pattern 255 chars long (16 * 15 + 15). */
224 for (counter = 0; counter < 15; counter++) {
225 strcat(expected, "0123456789ABCDEF");
226 }
227 strcat(expected, "0123456789ABCDE");
228 actual = statemachine_stop_record(sm);
229 ASSERT_STREQ(expected, actual);
230
231 statemachine_delete(sm);
232 return 0;
233}
234
235/* Test with characters outside of the ascii range */
236int test_no_ascii()
237{
238 statemachine_definition *def;
239 statemachine_ctx *sm;
240 def = statemachine_definition_new(NUM_STATES);
241 sm = statemachine_new(def, NULL);
242
243 statemachine_definition_populate(def, simple_state_transitions,
244 simple_states_internal_names);
245
246 ASSERT(sm->current_state == SIMPLE_STATE_A);
247
248 statemachine_parse(sm, "\xf0\xf0\xf1", 3);
249 ASSERT(sm->current_state == SIMPLE_STATE_B);
250
251 statemachine_parse(sm, "\xf0\xf0\xf1", 3);
252 ASSERT(sm->current_state == SIMPLE_STATE_C);
253
254 statemachine_parse(sm, "\xf2", 1);
255 ASSERT(sm->current_state == SIMPLE_STATE_B);
256
257 statemachine_parse(sm, "\xf1\xf1", 2);
258 ASSERT(sm->current_state == SIMPLE_STATE_D);
259
260 statemachine_delete(sm);
261 return 0;
262
263}
264
265int test_copy()
266{
267 statemachine_definition *def;
268 statemachine_ctx *sm1;
269 statemachine_ctx *sm2;
270 statemachine_ctx *sm3;
271 def = statemachine_definition_new(NUM_STATES);
272 sm1 = statemachine_new(def, NULL);
273
274 statemachine_definition_populate(def, simple_state_transitions,
275 simple_states_internal_names);
276
277 ASSERT(sm1->current_state == SIMPLE_STATE_A);
278
279 sm2 = statemachine_duplicate(sm1, def, NULL);
280 ASSERT(sm2->current_state == SIMPLE_STATE_A);
281
282 statemachine_parse(sm1, "001", 3);
283 ASSERT(sm1->current_state == SIMPLE_STATE_B);
284 ASSERT(sm2->current_state == SIMPLE_STATE_A);
285
286
287 statemachine_parse(sm1, "001", 3);
288 statemachine_parse(sm2, "001", 3);
289 ASSERT(sm1->current_state == SIMPLE_STATE_C);
290 ASSERT(sm2->current_state == SIMPLE_STATE_B);
291
292 sm3 = statemachine_duplicate(sm2, def, NULL);
293 ASSERT(sm3->current_state == SIMPLE_STATE_B);
294
295 statemachine_parse(sm1, "001", 3);
296 statemachine_parse(sm2, "001", 3);
297 statemachine_parse(sm3, "002", 3);
298 ASSERT(sm1->current_state == SIMPLE_STATE_D);
299 ASSERT(sm2->current_state == SIMPLE_STATE_C);
300 ASSERT(sm3->current_state == SIMPLE_STATE_A);
301
302 statemachine_delete(sm1);
303 statemachine_delete(sm2);
304 statemachine_delete(sm3);
305
306 return 0;
307}
308
309/* Tests statemachine_encode_char().
310 */
311int test_encode_char()
312{
313 char encoded_char[10];
314 int i;
315
316 struct {
317 char chr;
318 const char *result;
319 } encode_map[] = {
320 { 'x', "x" },
321 { '0', "0" },
322 { '\n', "\\n" },
323 { '\r', "\\r" },
324 { '\t', "\\t" },
325 { '\\', "\\\\" },
326 { '\0', "\\x00" },
327 { '\xF0', "\\xf0" },
328 { '\0', NULL} // Terminates when output == NULL
329 };
330
331 for (i = 0; encode_map[i].result; i++) {
332 statemachine_encode_char(encode_map[i].chr, encoded_char,
333 sizeof(encoded_char) / sizeof(*encoded_char));
334 ASSERT_STREQ(encoded_char, encode_map[i].result);
335 }
336
337 statemachine_encode_char('\xFF', encoded_char, 1);
338 ASSERT_STREQ(encoded_char, "");
339
340 statemachine_encode_char('\xFF', encoded_char, 2);
341 ASSERT_STREQ(encoded_char, "\\");
342
343 statemachine_encode_char('\xFF', encoded_char, 3);
344 ASSERT_STREQ(encoded_char, "\\x");
345
346 statemachine_encode_char('\xFF', encoded_char, 4);
347 ASSERT_STREQ(encoded_char, "\\xf");
348
349 statemachine_encode_char('\xFF', encoded_char, 5);
350 ASSERT_STREQ(encoded_char, "\\xff");
351
352 return 0;
353}
354
355int main(int argc, char **argv)
356{
357 test_simple();
358 test_error();
359 test_record();
360 test_no_ascii();
361 test_copy();
362 test_encode_char();
363 printf("DONE.\n");
364 return 0;
365}