Thu, 16 Jan 2020 19:14:53 +0100
add test for xml iterator
--- a/src/server/test/main.c Tue Jan 14 22:05:34 2020 +0100 +++ b/src/server/test/main.c Thu Jan 16 19:14:53 2020 +0100 @@ -42,6 +42,7 @@ #include <ucx/test.h> #include "vfs.h" +#include "xml.h" #include "webdav.h" void test() { @@ -68,6 +69,9 @@ ucx_test_register(suite, test_vfs_opendir); ucx_test_register(suite, test_vfs_readdir); + // xml tests + ucx_test_register(suite, test_wsxml_iterator); + // webdav tests ucx_test_register(suite, test_propfind_parse); ucx_test_register(suite, test_proppatch_parse);
--- a/src/server/test/objs.mk Tue Jan 14 22:05:34 2020 +0100 +++ b/src/server/test/objs.mk Thu Jan 16 19:14:53 2020 +0100 @@ -34,6 +34,7 @@ TESTOBJ += testutils.o TESTOBJ += webdav.o TESTOBJ += vfs.o +TESTOBJ += xml.o TESTOBJS = $(TESTOBJ:%=$(TEST_OBJPRE)%) TESTSOURCE = $(TESTOBJ:%.o=test/%.c)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/test/xml.c Thu Jan 16 19:14:53 2020 +0100 @@ -0,0 +1,167 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2020 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "xml.h" + +#include <stdio.h> +#include <stdlib.h> + +#include <libxml/tree.h> + +#include "testutils.h" + +#include "../public/webdav.h" + +typedef struct Test1Data { + int beginCounter; + int endCounter; + int elmCounter; + int endElmCounter; + int textErr; + int err; + int endErr; + xmlNode *prev; +} Test1Data; + +static int test1_begin(xmlNode *node, void *userdata) { + Test1Data *data = userdata; + data->beginCounter++; + + if(node->type == XML_ELEMENT_NODE) { + data->elmCounter++; + const char *name = (const char*)node->name; + + if(!strcmp(name, "ignore") || !strcmp(name, "ignore")) { + data->err = 1; + return 1; + } + + switch(data->elmCounter) { + case 1: if(strcmp(name, "test")){ data->err = 1; return 1; } break; + case 2: if(strcmp(name, "elm1")) { data->err = 1; return 1; } break; + case 3: if(strcmp(name, "elm2")) { data->err = 1; return 1; } break; + case 4: if(strcmp(name, "c")) { data->err = 1; return 1; } break; + case 5: if(strcmp(name, "a")) { data->err = 1; return 1; } break; + case 6: if(strcmp(name, "d")) { data->err = 1; return 1; } break; + case 7: if(strcmp(name, "e")) { data->err = 1; return 1; } break; + case 8: if(strcmp(name, "b")) { data->err = 1; return 1; } break; + case 9: if(strcmp(name, "x")) { data->err = 1; return 1; } break; + case 10: if(strcmp(name, "z")) { data->err = 1; return 1; } break; + case 11: if(strcmp(name, "nextelm")) { data->err = 1; return 1; } break; + } + } else if(node->type == XML_TEXT_NODE) { + const char *text = (const char*)node->content; + if(!strcmp(text, "teststr")) { + if(strcmp((const char*)data->prev->name, "elm1")) { + data->textErr = 1; + return 1; + } + } else if(!strcmp(text, "hello") || !strcmp(text, "world")) { + if(strcmp((const char*)data->prev->name, "a")) { + data->textErr = 1; + return 1; + } + } + } + + if(node->type == XML_ELEMENT_NODE) { + data->prev = node; + } + return 0; +} + +static int test1_end(xmlNode *node, void *userdata) { + Test1Data *data = userdata; + data->endCounter++; + + if(node->type == XML_ELEMENT_NODE) { + data->endElmCounter++; + const char *name = (const char*)node->name; + + if(!strcmp(name, "ignore") || !strcmp(name, "ignore")) { + data->err = 1; + return 1; + } + + switch(data->endElmCounter) { + case 1: if(strcmp(name, "elm1")){ data->endErr = 1; return 1; } break; + case 2: if(strcmp(name, "elm2")){ data->endErr = 1; return 1; } break; + case 3: if(strcmp(name, "a")){ data->endErr = 1; return 1; } break; + case 4: if(strcmp(name, "b")){ data->endErr = 1; return 1; } break; + case 5: if(strcmp(name, "e")){ data->endErr = 1; return 1; } break; + case 6: if(strcmp(name, "d")){ data->endErr = 1; return 1; } break; + case 7: if(strcmp(name, "c")){ data->endErr = 1; return 1; } break; + case 8: if(strcmp(name, "z")){ data->endErr = 1; return 1; } break; + case 9: if(strcmp(name, "x")){ data->endErr = 1; return 1; } break; + case 10: if(strcmp(name, "test")){ data->endErr = 1; return 1; } break; + case 11: if(strcmp(name, "nextelm")) { data->endErr = 1; return 1; } break; + } + } + + return 0; +} + +UCX_TEST(test_wsxml_iterator) { + Session *sn = testutil_session(); + + UCX_TEST_BEGIN; + + xmlDoc *doc = xmlReadMemory( + XML_TESTDATA1, strlen(XML_TESTDATA1), NULL, NULL, 0); + xmlDoc *doc2 = xmlReadMemory( + XML_TESTDATA2, strlen(XML_TESTDATA2), NULL, NULL, 0); + UCX_TEST_ASSERT(doc, "doc is NULL"); + UCX_TEST_ASSERT(doc2, "doc2 is NULL"); + + xmlNode *root = xmlDocGetRootElement(doc); + + // Test 1: iterate over complete document + Test1Data testdata; + ZERO(&testdata, sizeof(Test1Data)); + int ret = wsxml_iterator(sn->pool, root, test1_begin, test1_end, &testdata); + UCX_TEST_ASSERT(ret == 0, "wsxml_iterator failed"); + UCX_TEST_ASSERT(!testdata.err, "wrong element order (begin)"); + UCX_TEST_ASSERT(!testdata.endErr, "wrong element order (end)"); + UCX_TEST_ASSERT(!testdata.textErr, "text order error"); + UCX_TEST_ASSERT(testdata.beginCounter == testdata.endCounter, "begin/end counter not equal"); + + // Test 2: iterate over sub-document + ZERO(&testdata, sizeof(Test1Data)); + xmlNode *root2 = xmlDocGetRootElement(doc2); + xmlNode *sub = root2->children->children; + ret = wsxml_iterator(sn->pool, sub, test1_begin, test1_end, &testdata); + UCX_TEST_ASSERT(ret == 0, "test2: wsxml_iterator failed"); + UCX_TEST_ASSERT(!testdata.err, "test2: wrong element order (begin)"); + UCX_TEST_ASSERT(!testdata.endErr, "test2: wrong element order (end)"); + UCX_TEST_ASSERT(!testdata.textErr, "test2: text order error"); + UCX_TEST_ASSERT(testdata.beginCounter == testdata.endCounter, "test2: begin/end counter not equal"); + + xmlFreeDoc(doc); + xmlFreeDoc(doc2); + UCX_TEST_END; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/test/xml.h Thu Jan 16 19:14:53 2020 +0100 @@ -0,0 +1,74 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2020 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TEST_XML_H +#define TEST_XML_H + +#include "../public/nsapi.h" +#include <ucx/test.h> + +#ifdef __cplusplus +extern "C" { +#endif + +UCX_TEST(test_wsxml_iterator); + + +#define XML_TESTDATA1 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \ + <test> \ + <elm1>teststr</elm1> \ + <!-- comment -->\ + <elm2 /> \ + <c> \ + <a>hello</a> \ + world\ + <d><e><b/></e></d>\ + </c> \ + <x><z/></x> \ + </test>" + +#define XML_TESTDATA2 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \ + <root><wrapper><test> \ + <elm1>teststr</elm1> \ + <!-- comment -->\ + <elm2 /> \ + <c> \ + <a>hello</a> \ + world\ + <d><e><b/></e></d>\ + </c> \ + <x><z/></x> \ + </test><nextelm/></wrapper><ignore/></root>" + + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_XML_H */ +
--- a/src/server/webdav/objs.mk Tue Jan 14 22:05:34 2020 +0100 +++ b/src/server/webdav/objs.mk Thu Jan 16 19:14:53 2020 +0100 @@ -31,6 +31,7 @@ DAV_OBJPRE = $(OBJ_DIR)$(DAV_SRC_DIR) DAVOBJ = webdav.o +DAVOBJ += xml.o DAVOBJ += requestparser.o DAVOBJ += operation.o DAVOBJ += multistatus.o
--- a/src/server/webdav/xml.c Tue Jan 14 22:05:34 2020 +0100 +++ b/src/server/webdav/xml.c Thu Jan 16 19:14:53 2020 +0100 @@ -30,12 +30,12 @@ #include <stdlib.h> #include <string.h> - #include "xml.h" typedef struct StackElm { WSXmlNode *node; // list of nodes - WSXmlNode *parent; // if not NULL, call endcb after node->next is NULL + //WSXmlNode *parent; // if not NULL, call endcb after node->next is NULL + int endonly; struct StackElm *next; } StackElm; @@ -58,7 +58,8 @@ } stack->next = NULL; stack->node = node; - stack->parent = NULL; + stack->endonly = 0; + //stack->parent = NULL; int ret = 0; int br = 0; @@ -68,7 +69,7 @@ stack = cur->next; // and remove it cur->next = NULL; - while(xmlnode) { + while(xmlnode && !cur->endonly) { // element begin callback if(begincb(xmlnode, udata)) { br = 1; @@ -88,20 +89,37 @@ newelm->node = xmlnode->children; // setting the parent will make sure endcb will be called // for the current xmlnode after all children are processed - newelm->parent = xmlnode; + //newelm->parent = xmlnode; + newelm->endonly = 0; // if xmlnode->next is not NULL, there are still nodes at // this level, therefore we have to put these also on the // stack // this way, the remaining nodes are processed after all - // children of the current node are processed + // children and the end tag are processed if(xmlnode->next) { - // reuse current StackElm - cur->node = xmlnode->next; - STACK_PUSH(stack, cur); - cur = NULL; + StackElm *nextelm = pool_malloc(pool, sizeof(StackElm)); + if(!nextelm) { + ret = 1; + br = 1; + break; + } + nextelm->node = xmlnode->next; + nextelm->next = NULL; + nextelm->endonly = 0; + STACK_PUSH(stack, nextelm); } + // we have to put the end tag of the current element + // on the stack to ensure endcb is called for the current + // element, after all children are processed + // reuse cur + cur->node = xmlnode; + cur->endonly = 1; + STACK_PUSH(stack, cur); + + cur = NULL; + // now we can put the children on the stack STACK_PUSH(stack, newelm); // break, because we don't want to process xmlnode->next now @@ -109,6 +127,7 @@ } else { // no children means, the end callback can be called directly // after the begin callback (no intermediate nodes) + cur->node = NULL; if(endcb(xmlnode, udata)) { br = 1; break; @@ -122,11 +141,11 @@ break; // break because of an error } - if(cur) { - if(cur->parent) { - if(endcb(cur->parent, udata)) { - break; - } + if(cur && cur->node) { + //xmlNode *endNode = cur->parent ? cur->parent : cur->node; + xmlNode *endNode = cur->node; + if(endcb(endNode, udata)) { + break; } pool_free(pool, cur); }