1 /*
2 * SysDB - t/unit/frontend/parser_test.c
3 * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 #include "frontend/connection.h"
29 #include "frontend/parser.h"
30 #include "core/store-private.h"
31 #include "core/object.h"
32 #include "libsysdb_test.h"
34 #include <check.h>
36 /*
37 * tests
38 */
40 START_TEST(test_parse)
41 {
42 struct {
43 const char *query;
44 int len;
45 int expected;
46 sdb_conn_state_t expected_cmd;
47 } golden_data[] = {
48 /* empty commands */
49 { NULL, -1, -1, 0 },
50 { "", -1, 0, 0 },
51 { ";", -1, 0, 0 },
52 { ";;", -1, 0, 0 },
54 /* valid commands */
55 { "FETCH 'host'", -1, 1, CONNECTION_FETCH },
56 { "LIST", -1, 1, CONNECTION_LIST },
57 { "LIST -- comment", -1, 1, CONNECTION_LIST },
58 { "LIST;", -1, 1, CONNECTION_LIST },
59 { "LIST; INVALID", 5, 1, CONNECTION_LIST },
61 { "LOOKUP hosts MATCHING "
62 "host = 'host'", -1, 1, CONNECTION_LOOKUP },
63 { "LOOKUP hosts MATCHING NOT "
64 "host = 'host'", -1, 1, CONNECTION_LOOKUP },
65 { "LOOKUP hosts MATCHING "
66 "host =~ 'p' AND "
67 "service =~ 'p'", -1, 1, CONNECTION_LOOKUP },
68 { "LOOKUP hosts MATCHING NOT "
69 "host =~ 'p' AND "
70 "service =~ 'p'", -1, 1, CONNECTION_LOOKUP },
71 { "LOOKUP hosts MATCHING "
72 "host =~ 'p' AND "
73 "service =~ 'p' OR "
74 "service =~ 'r'", -1, 1, CONNECTION_LOOKUP },
75 { "LOOKUP hosts MATCHING NOT "
76 "host =~ 'p' AND "
77 "service =~ 'p' OR "
78 "service =~ 'r'", -1, 1, CONNECTION_LOOKUP },
80 /* numeric constants */
81 { "LOOKUP hosts MATCHING "
82 "attribute.foo = "
83 "1234", -1, 1, CONNECTION_LOOKUP },
84 { "LOOKUP hosts MATCHING "
85 "attribute.foo != "
86 "+234", -1, 1, CONNECTION_LOOKUP },
87 { "LOOKUP hosts MATCHING "
88 "attribute.foo < "
89 "-234", -1, 1, CONNECTION_LOOKUP },
90 { "LOOKUP hosts MATCHING "
91 "attribute.foo > "
92 "12.4", -1, 1, CONNECTION_LOOKUP },
93 { "LOOKUP hosts MATCHING "
94 "attribute.foo <= "
95 "12. + .3", -1, 1, CONNECTION_LOOKUP },
96 { "LOOKUP hosts MATCHING "
97 "attribute.foo >= "
98 ".4", -1, 1, CONNECTION_LOOKUP },
99 { "LOOKUP hosts MATCHING "
100 "attribute.foo = "
101 "+12e3", -1, 1, CONNECTION_LOOKUP },
102 { "LOOKUP hosts MATCHING "
103 "attribute.foo = "
104 "+12e-3", -1, 1, CONNECTION_LOOKUP },
105 { "LOOKUP hosts MATCHING "
106 "attribute.foo = "
107 "-12e+3", -1, 1, CONNECTION_LOOKUP },
109 /* NULL */
110 { "LOOKUP hosts MATCHING "
111 "attribute.foo "
112 "IS NULL", -1, 1, CONNECTION_LOOKUP },
113 { "LOOKUP hosts MATCHING "
114 "attribute.foo "
115 "IS NOT NULL", -1, 1, CONNECTION_LOOKUP },
116 { "LOOKUP hosts MATCHING "
117 "NOT attribute.foo "
118 "IS NULL", -1, 1, CONNECTION_LOOKUP },
119 { "LOOKUP hosts MATCHING "
120 "host IS NULL", -1, -1, 0 },
121 { "LOOKUP hosts MATCHING "
122 "service IS NULL", -1, -1, 0 },
124 /* invalid numeric constants */
125 { "LOOKUP hosts MATCHING "
126 "attribute.foo = "
127 "+-12e+3", -1, -1, 0 },
128 { "LOOKUP hosts MATCHING "
129 "attribute.foo = "
130 "-12e-+3", -1, -1, 0 },
131 { "LOOKUP hosts MATCHING "
132 "attribute.foo = "
133 "e+3", -1, -1, 0 },
134 { "LOOKUP hosts MATCHING "
135 "attribute.foo = "
136 "3e", -1, -1, 0 },
137 /* following SQL standard, we don't support hex numbers */
138 { "LOOKUP hosts MATCHING "
139 "attribute.foo = "
140 "0x12", -1, -1, 0 },
142 /* invalid expressions */
143 { "LOOKUP hosts MATCHING "
144 "attribute.foo = "
145 "1.23 + 'foo'", -1, -1, 0 },
147 /* comments */
148 { "/* some comment */", -1, 0, 0 },
149 { "-- another comment", -1, 0, 0 },
151 /* syntax errors */
152 { "INVALID", -1, -1, 0 },
153 { "FETCH host", -1, -1, 0 },
154 { "LIST; INVALID", 8, -1, 0 },
155 { "/* some incomplete", -1, -1, 0 },
157 { "LOOKUP hosts", -1, -1, 0 },
158 { "LOOKUP foo MATCHING "
159 "host = 'host'", -1, -1, 0 },
160 };
162 size_t i;
163 sdb_llist_t *check;
165 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
166 sdb_object_t *obj;
167 _Bool ok;
169 check = sdb_fe_parse(golden_data[i].query, golden_data[i].len);
170 if (golden_data[i].expected < 0)
171 ok = check == 0;
172 else
173 ok = sdb_llist_len(check) == (size_t)golden_data[i].expected;
175 fail_unless(ok, "sdb_fe_parse(%s) = %p (len: %zu); expected: %d",
176 golden_data[i].query, check, sdb_llist_len(check),
177 golden_data[i].expected);
179 if (! check)
180 continue;
182 if ((! golden_data[i].expected_cmd)
183 || (golden_data[i].expected <= 0)) {
184 sdb_llist_destroy(check);
185 continue;
186 }
188 obj = sdb_llist_get(check, 0);
189 fail_unless(SDB_CONN_NODE(obj)->cmd == golden_data[i].expected_cmd,
190 "sdb_fe_parse(%s)->cmd = %i; expected: %d",
191 golden_data[i].query, SDB_CONN_NODE(obj)->cmd,
192 golden_data[i].expected_cmd);
193 sdb_object_deref(obj);
194 sdb_llist_destroy(check);
195 }
196 }
197 END_TEST
199 START_TEST(test_parse_matcher)
200 {
201 struct {
202 const char *expr;
203 int len;
204 int expected;
205 } golden_data[] = {
206 /* empty expressions */
207 { NULL, -1, -1 },
208 { "", -1, -1 },
210 /* valid expressions */
211 { "host = 'localhost'", -1, MATCHER_NAME },
212 { "host != 'localhost'", -1, MATCHER_NOT },
213 { "host =~ 'host'", -1, MATCHER_NAME },
214 { "host !~ 'host'", -1, MATCHER_NOT },
215 { "host = 'localhost' -- foo", -1, MATCHER_NAME },
216 { "host = 'host' <garbage>", 13, MATCHER_NAME },
217 /* match hosts by service */
218 { "service = 'name'", -1, MATCHER_NAME },
219 { "service != 'name'", -1, MATCHER_NOT },
220 { "service =~ 'pattern'", -1, MATCHER_NAME },
221 { "service !~ 'pattern'", -1, MATCHER_NOT },
222 /* match hosts by attribute */
223 { "attribute = 'name'", -1, MATCHER_NAME },
224 { "attribute != 'name'", -1, MATCHER_NOT },
225 { "attribute =~ 'pattern'", -1, MATCHER_NAME },
226 { "attribute !~ 'pattern'", -1, MATCHER_NOT },
227 /* composite expressions */
228 { "host =~ 'pattern' AND "
229 "service =~ 'pattern'", -1, MATCHER_AND },
230 { "host =~ 'pattern' OR "
231 "service =~ 'pattern'", -1, MATCHER_OR },
232 { "NOT host = 'host'", -1, MATCHER_NOT },
233 /* numeric expressions */
234 { "attribute.foo < 123", -1, MATCHER_LT },
235 { "attribute.foo <= 123", -1, MATCHER_LE },
236 { "attribute.foo = 123", -1, MATCHER_EQ },
237 { "attribute.foo >= 123", -1, MATCHER_GE },
238 { "attribute.foo > 123", -1, MATCHER_GT },
239 /* NULL; while this is an implementation detail,
240 * IS NULL currently maps to an equality matcher */
241 { "attribute.foo IS NULL", -1, MATCHER_ISNULL },
242 { "attribute.foo IS NOT NULL", -1, MATCHER_NOT },
244 /* check operator precedence */
245 { "host = 'name' OR "
246 "service = 'name' AND "
247 "attribute = 'name' OR "
248 "attribute.foo = 'bar'", -1, MATCHER_OR },
249 { "host = 'name' AND "
250 "service = 'name' AND "
251 "attribute = 'name' OR "
252 "attribute.foo = 'bar'", -1, MATCHER_OR },
253 { "host = 'name' AND "
254 "service = 'name' OR "
255 "attribute = 'name' AND "
256 "attribute.foo = 'bar'", -1, MATCHER_OR },
257 { "(host = 'name' OR "
258 "service = 'name') AND "
259 "(attribute = 'name' OR "
260 "attribute.foo = 'bar')", -1, MATCHER_AND },
261 { "NOT host = 'name' OR "
262 "service = 'name'", -1, MATCHER_OR },
263 { "NOT host = 'name' OR "
264 "NOT service = 'name'", -1, MATCHER_OR },
265 { "NOT (host = 'name' OR "
266 "NOT service = 'name')", -1, MATCHER_NOT },
268 /* syntax errors */
269 { "LIST", -1, -1 },
270 { "foo &^ bar", -1, -1 },
271 };
273 size_t i;
275 for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
276 sdb_store_matcher_t *m;
277 m = sdb_fe_parse_matcher(golden_data[i].expr, golden_data[i].len);
279 if (golden_data[i].expected < 0) {
280 fail_unless(m == NULL,
281 "sdb_fe_parse_matcher(%s) = %p; expected: NULL",
282 golden_data[i].expr, m);
283 continue;
284 }
286 fail_unless(m != NULL, "sdb_fe_parse_matcher(%s) = NULL; "
287 "expected: <matcher>", golden_data[i].expr);
288 fail_unless(M(m)->type == golden_data[i].expected,
289 "sdb_fe_parse_matcher(%s) returned matcher of type %d; "
290 "expected: %d", golden_data[i].expr, M(m)->type,
291 golden_data[i].expected);
293 sdb_object_deref(SDB_OBJ(m));
294 }
295 }
296 END_TEST
298 Suite *
299 fe_parser_suite(void)
300 {
301 Suite *s = suite_create("frontend::parser");
302 TCase *tc;
304 tc = tcase_create("core");
305 tcase_add_test(tc, test_parse);
306 tcase_add_test(tc, test_parse_matcher);
307 suite_add_tcase(s, tc);
309 return s;
310 } /* util_parser_suite */
312 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */