blob: 37a690b4b8d88c7ee2cd7fc6ba687991caf156d9 [file] [log] [blame]
Radek Krejcid91dbaf2018-09-21 15:51:39 +02001/*
2 * @file xml.c
3 * @author: Radek Krejci <rkrejci@cesnet.cz>
4 * @brief unit tests for functions from xml.c
5 *
6 * Copyright (c) 2018 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
15#define _BSD_SOURCE
16#define _DEFAULT_SOURCE
17#include <stdarg.h>
18#include <stddef.h>
19#include <setjmp.h>
20#include <cmocka.h>
21
22#include <stdio.h>
23#include <string.h>
24
25#include "libyang.h"
26#include "../../src/xml.c"
27
28#define BUFSIZE 1024
29char logbuf[BUFSIZE] = {0};
30
31/* set to 0 to printing error messages to stderr instead of checking them in code */
32#define ENABLE_LOGGER_CHECKING 1
33
34static void
35logger(LY_LOG_LEVEL level, const char *msg, const char *path)
36{
37 (void) level; /* unused */
38
39 if (path) {
40 snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
41 } else {
42 strncpy(logbuf, msg, BUFSIZE - 1);
43 }
44}
45
46static int
47logger_setup(void **state)
48{
49 (void) state; /* unused */
50#if ENABLE_LOGGER_CHECKING
51 ly_set_log_clb(logger, 1);
52#endif
53 return 0;
54}
55
56void
57logbuf_clean(void)
58{
59 logbuf[0] = '\0';
60}
61
62#if ENABLE_LOGGER_CHECKING
63# define logbuf_assert(str) assert_string_equal(logbuf, str)
64#else
65# define logbuf_assert(str)
66#endif
67
Radek Krejcifb7c6582018-09-21 16:12:45 +020068
69static void
70test_utf8(void **state)
71{
72 (void) state; /* unused */
73
74 char buf[5] = {0};
75 const char *str = buf;
76 unsigned int c;
77 size_t len;
78
79 /* test invalid UTF-8 characters in lyxml_getutf8
80 * - https://en.wikipedia.org/wiki/UTF-8 */
Radek Krejcide362c02018-09-21 16:27:37 +020081 buf[0] = 0x04;
82 assert_int_equal(LY_EINVAL, lyxml_getutf8(&str, &c, &len));
Radek Krejcifb7c6582018-09-21 16:12:45 +020083 buf[0] = 0x80;
84 assert_int_equal(LY_EINVAL, lyxml_getutf8(&str, &c, &len));
85
Radek Krejci234ace02018-09-21 17:01:12 +020086 buf[0] = 0xc0;
Radek Krejcide362c02018-09-21 16:27:37 +020087 buf[1] = 0x00;
Radek Krejcifb7c6582018-09-21 16:12:45 +020088 assert_int_equal(LY_EINVAL, lyxml_getutf8(&str, &c, &len));
89 buf[1] = 0x80;
90 assert_int_equal(LY_EINVAL, lyxml_getutf8(&str, &c, &len));
91
Radek Krejci234ace02018-09-21 17:01:12 +020092 buf[0] = 0xe0;
93 buf[1] = 0x00;
Radek Krejcifb7c6582018-09-21 16:12:45 +020094 buf[2] = 0x80;
95 assert_int_equal(LY_EINVAL, lyxml_getutf8(&str, &c, &len));
Radek Krejci234ace02018-09-21 17:01:12 +020096 buf[1] = 0x80;
97 assert_int_equal(LY_EINVAL, lyxml_getutf8(&str, &c, &len));
98
99 buf[0] = 0xf0;
100 buf[1] = 0x00;
101 buf[2] = 0x80;
102 buf[3] = 0x80;
103 assert_int_equal(LY_EINVAL, lyxml_getutf8(&str, &c, &len));
104 buf[1] = 0x80;
105 assert_int_equal(LY_EINVAL, lyxml_getutf8(&str, &c, &len));
Radek Krejcifb7c6582018-09-21 16:12:45 +0200106}
107
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200108static void
109test_element(void **state)
110{
111 (void) state; /* unused */
112
113 size_t name_len, prefix_len;
114 const char *name, *prefix;
115 const char *str, *p;
116
117 struct lyxml_context ctx;
118 memset(&ctx, 0, sizeof ctx);
119 ctx.line = 1;
120
121 /* empty */
122 str = "";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200123 assert_int_equal(LY_SUCCESS, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200124 assert_null(name);
125 assert_true(str[0] == '\0');
126
127 /* no element */
128 logbuf_clean();
129 str = p = "no data present";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200130 assert_int_equal(LY_EINVAL, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200131 assert_null(name);
132 assert_ptr_equal(p, str); /* input data not eaten */
133 logbuf_assert("");
134
135 /* not supported DOCTYPE */
136 str = p = "<!DOCTYPE greeting SYSTEM \"hello.dtd\"><greeting/>";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200137 assert_int_equal(LY_EVALID, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200138 assert_null(name);
139 assert_ptr_equal(p, str); /* input data not eaten */
140 logbuf_assert("Document Type Declaration not supported. Line number 1.");
141
142 /* unqualified element */
143 str = " < element/>";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200144 assert_int_equal(LY_SUCCESS, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200145 assert_null(prefix);
146 assert_false(strncmp("element", name, name_len));
147 assert_int_equal(7, name_len);
148 assert_string_equal("/>", str);
149
Radek Krejcifb7c6582018-09-21 16:12:45 +0200150 str = "<?xml version=\"1.0\"?> <!-- comment --> <![CDATA[<greeting>Hello, world!</greeting>]]> <?TEST xxx?> <element/>";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200151 assert_int_equal(LY_SUCCESS, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200152 assert_null(prefix);
153 assert_false(strncmp("element", name, name_len));
154 assert_int_equal(7, name_len);
155 assert_string_equal("/>", str);
156
157 str = "<element xmlns=\"urn\"></element>";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200158 assert_int_equal(LY_SUCCESS, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200159 assert_null(prefix);
160 assert_false(strncmp("element", name, name_len));
161 assert_int_equal(7, name_len);
162 assert_string_equal(" xmlns=\"urn\"></element>", str);
163
164 /* qualified element */
165 str = " < yin:element/>";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200166 assert_int_equal(LY_SUCCESS, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200167 assert_false(strncmp("yin", prefix, prefix_len));
168 assert_false(strncmp("element", name, name_len));
169 assert_int_equal(3, prefix_len);
170 assert_int_equal(7, name_len);
171 assert_string_equal("/>", str);
172
173 str = "<yin:element xmlns=\"urn\"></element>";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200174 assert_int_equal(LY_SUCCESS, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200175 assert_false(strncmp("yin", prefix, prefix_len));
176 assert_false(strncmp("element", name, name_len));
177 assert_int_equal(3, prefix_len);
178 assert_int_equal(7, name_len);
179 assert_string_equal(" xmlns=\"urn\"></element>", str);
180
181 /* UTF8 characters */
182 str = "<𠜎€𠜎Øn:𠜎€𠜎Øn/>";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200183 assert_int_equal(LY_SUCCESS, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200184 assert_false(strncmp("𠜎€𠜎Øn", prefix, prefix_len));
185 assert_false(strncmp("𠜎€𠜎Øn", name, name_len));
186 assert_int_equal(14, prefix_len);
187 assert_int_equal(14, name_len);
188 assert_string_equal("/>", str);
189
190 /* invalid UTF-8 character */
191 str = "<¢:element>";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200192 assert_int_equal(LY_EVALID, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200193 logbuf_assert("Identifier \"¢:element>\" starts with invalid character. Line number 1.");
194 str = "<yin:c⁐element>";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200195 assert_int_equal(LY_EVALID, lyxml_get_element(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid972c252018-09-25 13:23:39 +0200196 logbuf_assert("Invalid character sequence \"⁐element>\", expected whitespace or element tag termination ('>' or '/>'. Line number 1.");
197}
198
199static void
200test_attribute(void **state)
201{
202 (void) state; /* unused */
203
204 size_t name_len, prefix_len;
205 const char *name, *prefix;
206 const char *str, *p;
207
208 struct lyxml_context ctx;
209 memset(&ctx, 0, sizeof ctx);
210 ctx.line = 1;
211
212 /* empty - without element tag termination */
213 str = "";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200214 assert_int_equal(LY_EINVAL, lyxml_get_attribute(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid972c252018-09-25 13:23:39 +0200215
216 /* empty - without element tag termination */
217 str = " />";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200218 assert_int_equal(LY_SUCCESS, lyxml_get_attribute(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid972c252018-09-25 13:23:39 +0200219 assert_null(name);
220 assert_true(str[0] == '/');
221 str = ">";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200222 assert_int_equal(LY_SUCCESS, lyxml_get_attribute(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid972c252018-09-25 13:23:39 +0200223 assert_null(name);
224 assert_true(str[0] == '>');
225
226 /* not an attribute */
227 str = p = "unknown/>";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200228 assert_int_equal(LY_EVALID, lyxml_get_attribute(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid972c252018-09-25 13:23:39 +0200229 assert_ptr_equal(p, str); /* input data not eaten */
230 logbuf_assert("Invalid character sequence \"/>\", expected whitespace or '='. Line number 1.");
231 str = p = "unknown />";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200232 assert_int_equal(LY_EVALID, lyxml_get_attribute(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid972c252018-09-25 13:23:39 +0200233 assert_ptr_equal(p, str); /* input data not eaten */
234 logbuf_assert("Invalid character sequence \"/>\", expected '='. Line number 1.");
235 str = p = "xxx=/>";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200236 assert_int_equal(LY_EVALID, lyxml_get_attribute(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid972c252018-09-25 13:23:39 +0200237 assert_ptr_equal(p, str); /* input data not eaten */
238 logbuf_assert("Invalid character sequence \"/>\", expected either single or double quotation mark. Line number 1.");
239 str = p = "xxx\n = yyy/>";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200240 assert_int_equal(LY_EVALID, lyxml_get_attribute(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid972c252018-09-25 13:23:39 +0200241 assert_ptr_equal(p, str); /* input data not eaten */
242 logbuf_assert("Invalid character sequence \"yyy/>\", expected either single or double quotation mark. Line number 2.");
243
244 /* valid attribute */
245 str = "xmlns=\"urn\">";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200246 assert_int_equal(LY_SUCCESS, lyxml_get_attribute(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid972c252018-09-25 13:23:39 +0200247 assert_non_null(name);
248 assert_null(prefix);
249 assert_int_equal(5, name_len);
250 assert_int_equal(0, prefix_len);
251 assert_false(strncmp("xmlns", name, name_len));
252 assert_string_equal("\"urn\">", str);
253
254 str = "xmlns:nc\n = \'urn\'>";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200255 assert_int_equal(LY_SUCCESS, lyxml_get_attribute(&ctx, &str, &prefix, &prefix_len, &name, &name_len));
Radek Krejcid972c252018-09-25 13:23:39 +0200256 assert_non_null(name);
257 assert_non_null(prefix);
258 assert_int_equal(2, name_len);
259 assert_int_equal(5, prefix_len);
260 assert_int_equal(3, ctx.line);
261 assert_false(strncmp("xmlns", prefix, prefix_len));
262 assert_false(strncmp("nc", name, name_len));
263 assert_string_equal("\'urn\'>", str);
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200264}
265
Radek Krejci7a7fa902018-09-25 17:08:21 +0200266static void
267test_text(void **state)
268{
269 (void) state; /* unused */
270
271 size_t out_len;
272 const char *str, *p;
273 char *out = NULL;
274
275 struct lyxml_context ctx;
276 memset(&ctx, 0, sizeof ctx);
277 ctx.line = 1;
278
279 /* empty attribute value */
280 str = "\"\"";
281 assert_int_equal(LY_SUCCESS, lyxml_get_string(&ctx, &str, &out, &out_len));
282 assert_non_null(out);
283 assert_int_equal(1, out_len);
284 assert_true(str[0] == '\0'); /* everything eaten */
285 assert_true(out[0] == '\0'); /* empty string */
286 str = "\'\'";
287 assert_int_equal(LY_SUCCESS, lyxml_get_string(&ctx, &str, &out, &out_len));
288 assert_non_null(out);
289 assert_int_equal(1, out_len);
290 assert_true(str[0] == '\0'); /* everything eaten */
291 assert_true(out[0] == '\0'); /* empty string */
292
Radek Krejcied6c6ad2018-09-26 09:10:18 +0200293 /* empty element content - only formating before defining child */
294 str = "\n <";
295 assert_int_equal(LY_EINVAL, lyxml_get_string(&ctx, &str, &out, &out_len));
296 assert_string_equal("<", str);
297
Radek Krejci7a7fa902018-09-25 17:08:21 +0200298 /* empty element content is invalid - missing content terminating character < */
299 str = "";
300 assert_int_equal(LY_EVALID, lyxml_get_string(&ctx, &str, &out, &out_len));
Radek Krejci117d2082018-09-26 10:05:14 +0200301 logbuf_assert("Unexpected end-of-file. Line number 2.");
Radek Krejcied6c6ad2018-09-26 09:10:18 +0200302
303 free(out);
304 out = NULL;
305
Radek Krejci117d2082018-09-26 10:05:14 +0200306 str = p = "xxx";
Radek Krejcied6c6ad2018-09-26 09:10:18 +0200307 assert_int_equal(LY_EVALID, lyxml_get_string(&ctx, &str, &out, &out_len));
Radek Krejci117d2082018-09-26 10:05:14 +0200308 logbuf_assert("Unexpected end-of-file. Line number 2.");
Radek Krejcied6c6ad2018-09-26 09:10:18 +0200309 assert_ptr_equal(p, str); /* input data not eaten */
Radek Krejci7a7fa902018-09-25 17:08:21 +0200310
311 free(out);
312 out = NULL;
313
314 /* valid strings */
Radek Krejcied6c6ad2018-09-26 09:10:18 +0200315 str = "€𠜎Øn \n&lt;&amp;&quot;&apos;&gt; &#82;&#x4f;&#x4B;<";
Radek Krejci7a7fa902018-09-25 17:08:21 +0200316 assert_int_equal(LY_SUCCESS, lyxml_get_string(&ctx, &str, &out, &out_len));
Radek Krejcied6c6ad2018-09-26 09:10:18 +0200317 assert_int_equal(22, out_len);
318 assert_string_equal("€𠜎Øn \n<&\"\'> ROK", out);
Radek Krejci7a7fa902018-09-25 17:08:21 +0200319 assert_string_equal("<", str);
320
Radek Krejci117d2082018-09-26 10:05:14 +0200321 /* test using n-bytes UTF8 hexadecimal code points */
322 str = "\'&#x0024;&#x00A2;&#x20ac;&#x10348;\'";
323 assert_int_equal(LY_SUCCESS, lyxml_get_string(&ctx, &str, &out, &out_len));
324 assert_string_equal("$¢€𐍈", out);
325
Radek Krejci7a7fa902018-09-25 17:08:21 +0200326 /* invalid characters in string */
327 str = p = "\'&#x52\'";
328 assert_int_equal(LY_EVALID, lyxml_get_string(&ctx, &str, &out, &out_len));
Radek Krejci117d2082018-09-26 10:05:14 +0200329 logbuf_assert("Invalid character sequence \"'\", expected ;. Line number 3.");
Radek Krejci7a7fa902018-09-25 17:08:21 +0200330 assert_ptr_equal(p, str); /* input data not eaten */
331 str = p = "\"&#82\"";
332 assert_int_equal(LY_EVALID, lyxml_get_string(&ctx, &str, &out, &out_len));
Radek Krejci117d2082018-09-26 10:05:14 +0200333 logbuf_assert("Invalid character sequence \"\"\", expected ;. Line number 3.");
Radek Krejci7a7fa902018-09-25 17:08:21 +0200334 assert_ptr_equal(p, str); /* input data not eaten */
Radek Krejcied6c6ad2018-09-26 09:10:18 +0200335 str = p = "\"&nonsence;\"";
336 assert_int_equal(LY_EVALID, lyxml_get_string(&ctx, &str, &out, &out_len));
Radek Krejci117d2082018-09-26 10:05:14 +0200337 logbuf_assert("Entity reference \"&nonsence;\" not supported, only predefined references allowed. Line number 3.");
Radek Krejcied6c6ad2018-09-26 09:10:18 +0200338 assert_ptr_equal(p, str); /* input data not eaten */
339 str = p = "&#o122;";
340 assert_int_equal(LY_EVALID, lyxml_get_string(&ctx, &str, &out, &out_len));
Radek Krejci117d2082018-09-26 10:05:14 +0200341 logbuf_assert("Invalid character reference \"&#o122;\". Line number 3.");
342 assert_ptr_equal(p, str); /* input data not eaten */
343
344 str = p = "\'&#x06;\'";
345 assert_int_equal(LY_EVALID, lyxml_get_string(&ctx, &str, &out, &out_len));
346 logbuf_assert("Invalid character reference \"&#x06;\'\" (0x00000006). Line number 3.");
347 assert_ptr_equal(p, str); /* input data not eaten */
348 str = p = "\'&#xfdd0;\'";
349 assert_int_equal(LY_EVALID, lyxml_get_string(&ctx, &str, &out, &out_len));
350 logbuf_assert("Invalid character reference \"&#xfdd0;\'\" (0x0000fdd0). Line number 3.");
351 assert_ptr_equal(p, str); /* input data not eaten */
352 str = p = "\'&#xffff;\'";
353 assert_int_equal(LY_EVALID, lyxml_get_string(&ctx, &str, &out, &out_len));
354 logbuf_assert("Invalid character reference \"&#xffff;\'\" (0x0000ffff). Line number 3.");
Radek Krejcied6c6ad2018-09-26 09:10:18 +0200355 assert_ptr_equal(p, str); /* input data not eaten */
Radek Krejci7a7fa902018-09-25 17:08:21 +0200356
357 free(out);
358}
359
Radek Krejci4b74d5e2018-09-26 14:30:55 +0200360static void
361test_ns(void **state)
362{
363 (void) state; /* unused */
364
365 const char *e1, *e2;
366 const struct lyxml_ns *ns;
367
368 struct lyxml_context ctx;
369 memset(&ctx, 0, sizeof ctx);
370 ctx.line = 1;
371
372 e1 = "element1";
373 e2 = "element2";
374 assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, e1, NULL, 0, strdup("urn:default")));
375 assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, e1, "nc", 2, strdup("urn:nc1")));
376 assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, e2, "nc", 2, strdup("urn:nc2")));
377 assert_int_equal(3, (&ctx)->ns.count);
378 assert_int_not_equal(0, (&ctx)->ns.size);
379
380 ns = lyxml_ns_get(&ctx, NULL, 0);
381 assert_non_null(ns);
382 assert_null(ns->prefix);
383 assert_string_equal("urn:default", ns->uri);
384
385 ns = lyxml_ns_get(&ctx, "nc", 2);
386 assert_non_null(ns);
387 assert_string_equal("nc", ns->prefix);
388 assert_string_equal("urn:nc2", ns->uri);
389
390 assert_int_equal(LY_SUCCESS, lyxml_ns_rm(&ctx, e2));
391 assert_int_equal(2, (&ctx)->ns.count);
392
393 ns = lyxml_ns_get(&ctx, "nc", 2);
394 assert_non_null(ns);
395 assert_string_equal("nc", ns->prefix);
396 assert_string_equal("urn:nc1", ns->uri);
397
398 assert_int_equal(LY_SUCCESS, lyxml_ns_rm(&ctx, e1));
399 assert_int_equal(0, (&ctx)->ns.count);
400
401 assert_null(lyxml_ns_get(&ctx, "nc", 2));
402 assert_null(lyxml_ns_get(&ctx, NULL, 0));
403}
404
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200405int main(void)
406{
407 const struct CMUnitTest tests[] = {
Radek Krejcifb7c6582018-09-21 16:12:45 +0200408 cmocka_unit_test_setup(test_utf8, logger_setup),
Radek Krejcid972c252018-09-25 13:23:39 +0200409 cmocka_unit_test_setup(test_element, logger_setup),
410 cmocka_unit_test_setup(test_attribute, logger_setup),
Radek Krejci7a7fa902018-09-25 17:08:21 +0200411 cmocka_unit_test_setup(test_text, logger_setup),
Radek Krejci4b74d5e2018-09-26 14:30:55 +0200412 cmocka_unit_test_setup(test_ns, logger_setup),
Radek Krejcid91dbaf2018-09-21 15:51:39 +0200413 };
414
415 return cmocka_run_group_tests(tests, NULL, NULL);
416}