1/*	$NetBSD: lex_test.c,v 1.2 2024/02/21 22:52:50 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16#include <inttypes.h>
17#include <sched.h> /* IWYU pragma: keep */
18#include <setjmp.h>
19#include <stdarg.h>
20#include <stddef.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24
25#define UNIT_TESTING
26#include <cmocka.h>
27
28#include <isc/buffer.h>
29#include <isc/lex.h>
30#include <isc/mem.h>
31#include <isc/util.h>
32
33#include <tests/isc.h>
34
35#define AS_STR(x) (x).value.as_textregion.base
36
37/* check handling of 0xff */
38ISC_RUN_TEST_IMPL(lex_0xff) {
39	isc_result_t result;
40	isc_lex_t *lex = NULL;
41	isc_buffer_t death_buf;
42	isc_token_t token;
43
44	unsigned char death[] = { EOF, 'A' };
45
46	UNUSED(state);
47
48	result = isc_lex_create(mctx, 1024, &lex);
49	assert_int_equal(result, ISC_R_SUCCESS);
50
51	isc_buffer_init(&death_buf, &death[0], sizeof(death));
52	isc_buffer_add(&death_buf, sizeof(death));
53
54	result = isc_lex_openbuffer(lex, &death_buf);
55	assert_int_equal(result, ISC_R_SUCCESS);
56
57	result = isc_lex_gettoken(lex, 0, &token);
58	assert_int_equal(result, ISC_R_SUCCESS);
59
60	isc_lex_destroy(&lex);
61}
62
63/* check setting of source line */
64ISC_RUN_TEST_IMPL(lex_setline) {
65	isc_result_t result;
66	isc_lex_t *lex = NULL;
67	unsigned char text[] = "text\nto\nbe\nprocessed\nby\nlexer";
68	isc_buffer_t buf;
69	isc_token_t token;
70	unsigned long line;
71	int i;
72
73	UNUSED(state);
74
75	result = isc_lex_create(mctx, 1024, &lex);
76	assert_int_equal(result, ISC_R_SUCCESS);
77
78	isc_buffer_init(&buf, &text[0], sizeof(text));
79	isc_buffer_add(&buf, sizeof(text));
80
81	result = isc_lex_openbuffer(lex, &buf);
82	assert_int_equal(result, ISC_R_SUCCESS);
83
84	result = isc_lex_setsourceline(lex, 100);
85	assert_int_equal(result, ISC_R_SUCCESS);
86
87	for (i = 0; i < 6; i++) {
88		result = isc_lex_gettoken(lex, 0, &token);
89		assert_int_equal(result, ISC_R_SUCCESS);
90
91		line = isc_lex_getsourceline(lex);
92		assert_int_equal(line, 100U + i);
93	}
94
95	result = isc_lex_gettoken(lex, 0, &token);
96	assert_int_equal(result, ISC_R_EOF);
97
98	line = isc_lex_getsourceline(lex);
99	assert_int_equal(line, 105U);
100
101	isc_lex_destroy(&lex);
102}
103
104static struct {
105	const char *text;
106	const char *string_value;
107	isc_result_t string_result;
108	isc_tokentype_t string_type;
109	const char *qstring_value;
110	isc_result_t qstring_result;
111	isc_tokentype_t qstring_type;
112	const char *qvpair_value;
113	isc_result_t qvpair_result;
114	isc_tokentype_t qvpair_type;
115} parse_tests[] = {
116	{ "", "", ISC_R_SUCCESS, isc_tokentype_eof, "", ISC_R_SUCCESS,
117	  isc_tokentype_eof, "", ISC_R_SUCCESS, isc_tokentype_eof },
118	{ "1234", "1234", ISC_R_SUCCESS, isc_tokentype_string, "1234",
119	  ISC_R_SUCCESS, isc_tokentype_string, "1234", ISC_R_SUCCESS,
120	  isc_tokentype_string },
121	{ "1234=", "1234=", ISC_R_SUCCESS, isc_tokentype_string,
122	  "1234=", ISC_R_SUCCESS, isc_tokentype_string, "1234=", ISC_R_SUCCESS,
123	  isc_tokentype_vpair },
124	{ "1234=foo", "1234=foo", ISC_R_SUCCESS, isc_tokentype_string,
125	  "1234=foo", ISC_R_SUCCESS, isc_tokentype_string, "1234=foo",
126	  ISC_R_SUCCESS, isc_tokentype_vpair },
127	{ "1234=\"foo", "1234=\"foo", ISC_R_SUCCESS, isc_tokentype_string,
128	  "1234=\"foo", ISC_R_SUCCESS, isc_tokentype_string, NULL,
129	  ISC_R_UNEXPECTEDEND, 0 },
130	{ "1234=\"foo\"", "1234=\"foo\"", ISC_R_SUCCESS, isc_tokentype_string,
131	  "1234=\"foo\"", ISC_R_SUCCESS, isc_tokentype_string, "1234=foo",
132	  ISC_R_SUCCESS, isc_tokentype_qvpair },
133	{ "key", "key", ISC_R_SUCCESS, isc_tokentype_string, "key",
134	  ISC_R_SUCCESS, isc_tokentype_string, "key", ISC_R_SUCCESS,
135	  isc_tokentype_string },
136	{ "\"key=", "\"key=", ISC_R_SUCCESS, isc_tokentype_string, NULL,
137	  ISC_R_UNEXPECTEDEND, 0, "\"key=", ISC_R_SUCCESS,
138	  isc_tokentype_vpair },
139	{ "\"key=\"", "\"key=\"", ISC_R_SUCCESS, isc_tokentype_string, "key=",
140	  ISC_R_SUCCESS, isc_tokentype_qstring, NULL, ISC_R_UNEXPECTEDEND, 0 },
141	{ "key=\"\"", "key=\"\"", ISC_R_SUCCESS, isc_tokentype_string,
142	  "key=\"\"", ISC_R_SUCCESS, isc_tokentype_string,
143	  "key=", ISC_R_SUCCESS, isc_tokentype_qvpair },
144	{ "key=\"a b\"", "key=\"a", ISC_R_SUCCESS, isc_tokentype_string,
145	  "key=\"a", ISC_R_SUCCESS, isc_tokentype_string, "key=a b",
146	  ISC_R_SUCCESS, isc_tokentype_qvpair },
147	{ "key=\"a\tb\"", "key=\"a", ISC_R_SUCCESS, isc_tokentype_string,
148	  "key=\"a", ISC_R_SUCCESS, isc_tokentype_string, "key=a\tb",
149	  ISC_R_SUCCESS, isc_tokentype_qvpair },
150	/* double quote not immediately after '=' is not special. */
151	{ "key=c\"a b\"", "key=c\"a", ISC_R_SUCCESS, isc_tokentype_string,
152	  "key=c\"a", ISC_R_SUCCESS, isc_tokentype_string, "key=c\"a",
153	  ISC_R_SUCCESS, isc_tokentype_vpair },
154	/* remove special meaning for '=' by escaping */
155	{ "key\\=", "key\\=", ISC_R_SUCCESS, isc_tokentype_string,
156	  "key\\=", ISC_R_SUCCESS, isc_tokentype_string,
157	  "key\\=", ISC_R_SUCCESS, isc_tokentype_string },
158	{ "key\\=\"a\"", "key\\=\"a\"", ISC_R_SUCCESS, isc_tokentype_string,
159	  "key\\=\"a\"", ISC_R_SUCCESS, isc_tokentype_string, "key\\=\"a\"",
160	  ISC_R_SUCCESS, isc_tokentype_string },
161	{ "key\\=\"a \"", "key\\=\"a", ISC_R_SUCCESS, isc_tokentype_string,
162	  "key\\=\"a", ISC_R_SUCCESS, isc_tokentype_string, "key\\=\"a",
163	  ISC_R_SUCCESS, isc_tokentype_string },
164	/* vpair with a key of 'key\=' (would need to be deescaped) */
165	{ "key\\==", "key\\==", ISC_R_SUCCESS, isc_tokentype_string,
166	  "key\\==", ISC_R_SUCCESS, isc_tokentype_string,
167	  "key\\==", ISC_R_SUCCESS, isc_tokentype_vpair },
168	{ "key\\==\"\"", "key\\==\"\"", ISC_R_SUCCESS, isc_tokentype_string,
169	  "key\\==\"\"", ISC_R_SUCCESS, isc_tokentype_string,
170	  "key\\==", ISC_R_SUCCESS, isc_tokentype_qvpair },
171	{ "key=\\\\\\\\", "key=\\\\\\\\", ISC_R_SUCCESS, isc_tokentype_string,
172	  "key=\\\\\\\\", ISC_R_SUCCESS, isc_tokentype_string, "key=\\\\\\\\",
173	  ISC_R_SUCCESS, isc_tokentype_vpair },
174	{ "key=\\\\\\\"", "key=\\\\\\\"", ISC_R_SUCCESS, isc_tokentype_string,
175	  "key=\\\\\\\"", ISC_R_SUCCESS, isc_tokentype_string, "key=\\\\\\\"",
176	  ISC_R_SUCCESS, isc_tokentype_vpair },
177	/* incomplete escape sequence */
178	{ "key=\\\"\\", NULL, ISC_R_UNEXPECTEDEND, isc_tokentype_string, NULL,
179	  ISC_R_UNEXPECTEDEND, 0, NULL, ISC_R_UNEXPECTEDEND, 0 },
180	/* incomplete escape sequence */
181	{ "key=\\", NULL, ISC_R_UNEXPECTEDEND, isc_tokentype_string, NULL,
182	  ISC_R_UNEXPECTEDEND, 0, NULL, ISC_R_UNEXPECTEDEND, 0 },
183};
184
185/*%
186 * string
187 */
188ISC_RUN_TEST_IMPL(lex_string) {
189	isc_buffer_t buf;
190	isc_lex_t *lex = NULL;
191	isc_result_t result;
192	isc_token_t token;
193	size_t i;
194
195	UNUSED(state);
196
197	for (i = 0; i < ARRAY_SIZE(parse_tests); i++) {
198		result = isc_lex_create(mctx, 1024, &lex);
199		assert_int_equal(result, ISC_R_SUCCESS);
200
201		isc_buffer_constinit(&buf, parse_tests[i].text,
202				     strlen(parse_tests[i].text));
203		isc_buffer_add(&buf, strlen(parse_tests[i].text));
204
205		result = isc_lex_openbuffer(lex, &buf);
206		assert_int_equal(result, ISC_R_SUCCESS);
207
208		result = isc_lex_setsourceline(lex, 100);
209		assert_int_equal(result, ISC_R_SUCCESS);
210
211		memset(&token, 0, sizeof(token));
212		result = isc_lex_getmastertoken(lex, &token,
213						isc_tokentype_string, true);
214
215		assert_int_equal(result, parse_tests[i].string_result);
216		if (result == ISC_R_SUCCESS) {
217			switch (token.type) {
218			case isc_tokentype_string:
219			case isc_tokentype_qstring:
220			case isc_tokentype_vpair:
221			case isc_tokentype_qvpair:
222				assert_int_equal(token.type,
223						 parse_tests[i].string_type);
224				assert_string_equal(
225					AS_STR(token),
226					parse_tests[i].string_value);
227				break;
228			default:
229				assert_int_equal(token.type,
230						 parse_tests[i].string_type);
231				break;
232			}
233		}
234
235		isc_lex_destroy(&lex);
236	}
237}
238
239/*%
240 * qstring
241 */
242ISC_RUN_TEST_IMPL(lex_qstring) {
243	isc_buffer_t buf;
244	isc_lex_t *lex = NULL;
245	isc_result_t result;
246	isc_token_t token;
247	size_t i;
248
249	UNUSED(state);
250
251	for (i = 0; i < ARRAY_SIZE(parse_tests); i++) {
252		result = isc_lex_create(mctx, 1024, &lex);
253		assert_int_equal(result, ISC_R_SUCCESS);
254
255		isc_buffer_constinit(&buf, parse_tests[i].text,
256				     strlen(parse_tests[i].text));
257		isc_buffer_add(&buf, strlen(parse_tests[i].text));
258
259		result = isc_lex_openbuffer(lex, &buf);
260		assert_int_equal(result, ISC_R_SUCCESS);
261
262		result = isc_lex_setsourceline(lex, 100);
263		assert_int_equal(result, ISC_R_SUCCESS);
264
265		memset(&token, 0, sizeof(token));
266		result = isc_lex_getmastertoken(lex, &token,
267						isc_tokentype_qstring, true);
268
269		assert_int_equal(result, parse_tests[i].qstring_result);
270		if (result == ISC_R_SUCCESS) {
271			switch (token.type) {
272			case isc_tokentype_string:
273			case isc_tokentype_qstring:
274			case isc_tokentype_vpair:
275			case isc_tokentype_qvpair:
276				assert_int_equal(token.type,
277						 parse_tests[i].qstring_type);
278				assert_string_equal(
279					AS_STR(token),
280					parse_tests[i].qstring_value);
281				break;
282			default:
283				assert_int_equal(token.type,
284						 parse_tests[i].qstring_type);
285				break;
286			}
287		}
288
289		isc_lex_destroy(&lex);
290	}
291}
292
293/*%
294 * keypair is <string>=<qstring>.  This has implications double quotes
295 * in key names.
296 */
297ISC_RUN_TEST_IMPL(lex_keypair) {
298	isc_buffer_t buf;
299	isc_lex_t *lex = NULL;
300	isc_result_t result;
301	isc_token_t token;
302	size_t i;
303
304	UNUSED(state);
305
306	for (i = 0; i < ARRAY_SIZE(parse_tests); i++) {
307		result = isc_lex_create(mctx, 1024, &lex);
308		assert_int_equal(result, ISC_R_SUCCESS);
309
310		isc_buffer_constinit(&buf, parse_tests[i].text,
311				     strlen(parse_tests[i].text));
312		isc_buffer_add(&buf, strlen(parse_tests[i].text));
313
314		result = isc_lex_openbuffer(lex, &buf);
315		assert_int_equal(result, ISC_R_SUCCESS);
316
317		result = isc_lex_setsourceline(lex, 100);
318		assert_int_equal(result, ISC_R_SUCCESS);
319
320		memset(&token, 0, sizeof(token));
321		result = isc_lex_getmastertoken(lex, &token,
322						isc_tokentype_qvpair, true);
323
324		assert_int_equal(result, parse_tests[i].qvpair_result);
325		if (result == ISC_R_SUCCESS) {
326			switch (token.type) {
327			case isc_tokentype_string:
328			case isc_tokentype_qstring:
329			case isc_tokentype_vpair:
330			case isc_tokentype_qvpair:
331				assert_int_equal(token.type,
332						 parse_tests[i].qvpair_type);
333				assert_string_equal(
334					AS_STR(token),
335					parse_tests[i].qvpair_value);
336				break;
337			default:
338				assert_int_equal(token.type,
339						 parse_tests[i].qvpair_type);
340				break;
341			}
342		}
343
344		isc_lex_destroy(&lex);
345	}
346}
347
348ISC_TEST_LIST_START
349ISC_TEST_ENTRY(lex_0xff)
350ISC_TEST_ENTRY(lex_keypair)
351ISC_TEST_ENTRY(lex_setline)
352ISC_TEST_ENTRY(lex_string)
353ISC_TEST_ENTRY(lex_qstring)
354
355ISC_TEST_LIST_END
356
357ISC_TEST_MAIN
358