Sat, 22 Nov 2025 14:27:01 +0100
port old ucx2 tests to ucx3
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2019 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 <stdio.h> #include <stdlib.h> #include <string.h> #include "testutils.h" #include "../webdav/requestparser.h" #include "../webdav/webdav.h" #include "../webdav/multistatus.h" #include "../webdav/operation.h" #include "vfs.h" #include "webdav.h" static int webdav_is_initialized = 0; /* ----------------------------- Test Backends --------------------------*/ static int backend2_init_called = 0; static int backend2_propfind_do_count = 0; static int backend2_propfind_finish_called = 0; static int backend2_proppatch_commit = 0; static int backend2_proppatch_do_count = 0; static int backend2_proppatch_finish_count = 0; // backend2 static int backend2_propfind_init( WebdavPropfindRequest *propfind, const char *path, const char *href, WebdavPList **outPList) { backend2_init_called = 1; return 0; } static int backend2_propfind_do( WebdavPropfindRequest *propfind, WebdavResponse *response, VFS_DIR parent, WebdavResource *resource, struct stat *s) { backend2_propfind_do_count++; return 0; } static int backend2_propfind_finish(WebdavPropfindRequest *propfind) { backend2_propfind_finish_called = 1; return 0; } static int backend2_proppatch_do( WebdavProppatchRequest *request, WebdavResource *response, VFSFile *file, WebdavPList **out_set, WebdavPList **out_remove) { backend2_proppatch_do_count++; if(*out_remove) { return 1; // backend1 should remove all remove-props } WebdavPListIterator i = webdav_plist_iterator(out_set); WebdavPList *cur; while(webdav_plist_iterator_next(&i, &cur)) { if(!strcmp(cur->property->name, "a")) { // property 'a' should already be removed by backend1 return 1; } else if(!strcmp(cur->property->name, "abort")) { return 1; // test abort } response->addproperty(response, cur->property, 200); webdav_plist_iterator_remove_current(&i); } return 0; } static int backend2_proppatch_finish( WebdavProppatchRequest *request, WebdavResource *response, VFSFile *file, WSBool commit) { backend2_proppatch_finish_count++; backend2_proppatch_commit = commit; return 0; } static WebdavBackend backend2 = { backend2_propfind_init, backend2_propfind_do, backend2_propfind_finish, backend2_proppatch_do, backend2_proppatch_finish, NULL, // opt_mkcol NULL, // opt_mkcol_finish NULL, // opt_delete NULL, // opt_delete_finish 0, NULL, // instance NULL }; // backend1 static int backend1_init_called = 0; static int backend1_propfind_do_count = 0; static int backend1_propfind_finish_called = 0; static int backend1_proppatch_commit = 0; static int backend1_proppatch_do_count = 0; static int backend1_proppatch_finish_count = 0; static int backend1_propfind_init( WebdavPropfindRequest *propfind, const char *path, const char *href, WebdavPList **outPList) { backend1_init_called = 1; WebdavPList *plist = *outPList; WebdavProperty *p = plist->property; if(!strcmp(p->name, "displayname")) { plist->next->prev = NULL; *outPList = plist->next; // remove first item from plist } else { return 1; } return 0; } static int backend1_propfind_do( WebdavPropfindRequest *propfind, WebdavResponse *response, VFS_DIR parent, WebdavResource *resource, struct stat *s) { backend1_propfind_do_count++; return 0; } static int backend1_propfind_finish(WebdavPropfindRequest *propfind) { backend1_propfind_finish_called = 1; return 0; } static int backend1_proppatch_do( WebdavProppatchRequest *request, WebdavResource *response, VFSFile *file, WebdavPList **out_set, WebdavPList **out_remove) { backend1_proppatch_do_count++; // remove everything from out_remove WebdavPListIterator i = webdav_plist_iterator(out_remove); WebdavPList *cur; while(webdav_plist_iterator_next(&i, &cur)) { response->addproperty(response, cur->property, 200); webdav_plist_iterator_remove_current(&i); } // remove property 'a' and fail at property 'fail' i = webdav_plist_iterator(out_set); while(webdav_plist_iterator_next(&i, &cur)) { if(!strcmp(cur->property->name, "fail")) { response->addproperty(response, cur->property, 403); webdav_plist_iterator_remove_current(&i); } else if(!strcmp(cur->property->name, "a")) { response->addproperty(response, cur->property, 200); webdav_plist_iterator_remove_current(&i); } } return 0; } static int backend1_proppatch_finish( WebdavProppatchRequest *request, WebdavResource *response, VFSFile *file, WSBool commit) { backend1_proppatch_finish_count++; backend1_proppatch_commit = commit; return 0; } WebdavBackend backend1 = { backend1_propfind_init, backend1_propfind_do, backend1_propfind_finish, backend1_proppatch_do, backend1_proppatch_finish, NULL, // opt_mkcol NULL, // opt_mkcol_finish NULL, // opt_delete NULL, // opt_delete_finish 0, NULL, // instance &backend2 }; static void reset_backends(void) { backend1_init_called = 0; backend1_propfind_do_count = 0; backend1_propfind_finish_called = 0; backend1_proppatch_commit = 0; backend1_proppatch_do_count = 0; backend1_proppatch_finish_count = 0; backend2_init_called = 0; backend2_propfind_do_count = 0; backend2_propfind_finish_called = 0; backend2_proppatch_commit = 0; backend2_proppatch_do_count = 0; backend2_proppatch_finish_count = 0; } /* ----------------------------------------------------------------------*/ static int test_init( Session **out_sn, Request **out_rq, WebdavPropfindRequest **out_propfind, const char *xml) { if(!webdav_is_initialized) { if(webdav_init(NULL, NULL, NULL) != REQ_PROCEED) { return 1; } webdav_is_initialized = 1; } Session *sn = testutil_session(); Request *rq = testutil_request(sn->pool, "PROPFIND", "/"); int error = 0; WebdavPropfindRequest *propfind = propfind_parse( sn, rq, xml, strlen(xml), &error); if(error) { return 1; } if(!propfind || !propfind->properties) { return 1; } *out_sn = sn; *out_rq = rq; *out_propfind = propfind; return 0; } static WebdavOperation* test_propfind_op( Session **out_sn, Request **out_rq, const char *xml) { WebdavPropfindRequest *propfind; if(test_init(out_sn, out_rq, &propfind, xml)) { return NULL; } Multistatus *ms = multistatus_response(*out_sn, *out_rq); if(!ms) { return NULL; } // WebdavResponse is the public interface used by Backends // for adding resources to the response WebdavResponse *response = (WebdavResponse*)ms; WebdavPropfindRequestList *requests = NULL; // Initialize all Webdav Backends if(webdav_propfind_init(&backend1, propfind, "/", "/", &requests)) { return NULL; } return webdav_create_propfind_operation( (*out_sn), (*out_rq), &backend1, propfind->properties, requests, response); } CX_TEST(test_webdav_plist_add) { Session *sn = testutil_session(); CX_TEST_DO { WebdavPList *begin = NULL; WebdavPList *end = NULL; WebdavProperty p1, p2, p3; ZERO(&p1, sizeof(WebdavProperty)); ZERO(&p2, sizeof(WebdavProperty)); ZERO(&p3, sizeof(WebdavProperty)); int r; r = webdav_plist_add(sn->pool, &begin, &end, &p1); CX_TEST_ASSERT(r == 0); CX_TEST_ASSERT(begin && end); CX_TEST_ASSERT(begin == end); CX_TEST_ASSERT(begin->prev == NULL); CX_TEST_ASSERT(begin->next == NULL); r = webdav_plist_add(sn->pool, &begin, &end, &p2); CX_TEST_ASSERT(r == 0); CX_TEST_ASSERT(begin && end); CX_TEST_ASSERT(begin->next); CX_TEST_ASSERT(begin->next == end); CX_TEST_ASSERT(end->prev = begin); CX_TEST_ASSERT(begin->prev == NULL); CX_TEST_ASSERT(end->next == NULL); r = webdav_plist_add(sn->pool, &begin, &end, &p3); CX_TEST_ASSERT(r == 0); CX_TEST_ASSERT(begin && end); CX_TEST_ASSERT(begin->next == end->prev); } testutil_destroy_session(sn); } CX_TEST(test_webdav_plist_size) { Session *sn = testutil_session(); CX_TEST_DO { WebdavPList *begin = NULL; WebdavPList *end = NULL; WebdavProperty p1, p2, p3; ZERO(&p1, sizeof(WebdavProperty)); ZERO(&p2, sizeof(WebdavProperty)); ZERO(&p3, sizeof(WebdavProperty)); int r; CX_TEST_ASSERT(webdav_plist_size(begin) == 0); r = webdav_plist_add(sn->pool, &begin, &end, &p1); CX_TEST_ASSERT(webdav_plist_size(begin) == 1); r = webdav_plist_add(sn->pool, &begin, &end, &p2); CX_TEST_ASSERT(webdav_plist_size(begin) == 2); r = webdav_plist_add(sn->pool, &begin, &end, &p3); CX_TEST_ASSERT(webdav_plist_size(begin) == 3); } testutil_destroy_session(sn); } CX_TEST(test_propfind_parse) { Session *sn = testutil_session(); Request *rq = testutil_request(sn->pool, "PROPFIND", "/"); CX_TEST_DO { int error = 0; // // ----------------- TEST_PROPFIND1 ----------------- // test basic propfind request WebdavPropfindRequest *p1 = propfind_parse( sn, rq, TEST_PROPFIND1, strlen(TEST_PROPFIND1), &error); CX_TEST_ASSERT(p1); CX_TEST_ASSERT(p1->properties); CX_TEST_ASSERT(!p1->allprop); CX_TEST_ASSERT(!p1->propname); CX_TEST_ASSERT(p1->propcount == 6); // property 1: DAV:displayname WebdavPList *elm = p1->properties; CX_TEST_ASSERT( !strcmp(elm->property->name, "displayname")); CX_TEST_ASSERT( !strcmp((char*)elm->property->namespace->href, "DAV:")); // property 2: DAV:getcontentlength elm = elm->next; CX_TEST_ASSERT(elm); CX_TEST_ASSERT( !strcmp(elm->property->name, "getcontentlength")); CX_TEST_ASSERT( !strcmp((char*)elm->property->namespace->href, "DAV:")); elm = elm->next; CX_TEST_ASSERT(elm); elm = elm->next; CX_TEST_ASSERT(elm); elm = elm->next; CX_TEST_ASSERT(elm); // property 6: DAV:getetag elm = elm->next; CX_TEST_ASSERT(elm); CX_TEST_ASSERT( !strcmp(elm->property->name, "getetag")); CX_TEST_ASSERT( !strcmp((char*)elm->property->namespace->href, "DAV:")); CX_TEST_ASSERT(!elm->next); // // ----------------- TEST_PROPFIND2 ----------------- // test with multiple namespaces WebdavPropfindRequest *p2 = propfind_parse( sn, rq, TEST_PROPFIND2, strlen(TEST_PROPFIND2), &error); CX_TEST_ASSERT(p2); CX_TEST_ASSERT(p2->properties); CX_TEST_ASSERT(!p2->allprop); CX_TEST_ASSERT(!p2->propname); // property 1: DAV:resourcetype elm = p2->properties; CX_TEST_ASSERT( !strcmp(elm->property->name, "resourcetype")); CX_TEST_ASSERT( !strcmp((char*)elm->property->namespace->href, "DAV:")); // property 2: X:testprop elm = elm->next; CX_TEST_ASSERT(elm); CX_TEST_ASSERT( !strcmp(elm->property->name, "testprop")); CX_TEST_ASSERT( !strcmp((char*)elm->property->namespace->href, "http://example.com/")); // property 3: X:name elm = elm->next; CX_TEST_ASSERT(elm); CX_TEST_ASSERT( !strcmp(elm->property->name, "name")); CX_TEST_ASSERT( !strcmp((char*)elm->property->namespace->href, "http://example.com/")); // property 4: Z:testprop elm = elm->next; CX_TEST_ASSERT(elm); CX_TEST_ASSERT( !strcmp(elm->property->name, "testprop")); CX_TEST_ASSERT( !strcmp((char*)elm->property->namespace->href, "testns")); // // ----------------- TEST_PROPFIND3 ----------------- // test allprop WebdavPropfindRequest *p3 = propfind_parse(sn, rq, TEST_PROPFIND3, strlen(TEST_PROPFIND3), &error); CX_TEST_ASSERT(p3); CX_TEST_ASSERT(!p3->properties); CX_TEST_ASSERT(p3->allprop); CX_TEST_ASSERT(!p3->propname); CX_TEST_ASSERT(p3->propcount == 0); // // ----------------- TEST_PROPFIND4 ----------------- // test propname WebdavPropfindRequest *p4 = propfind_parse(sn, rq, TEST_PROPFIND4, strlen(TEST_PROPFIND4), &error); CX_TEST_ASSERT(p4); CX_TEST_ASSERT(!p4->properties); CX_TEST_ASSERT(!p4->allprop); CX_TEST_ASSERT(p4->propname); // // ----------------- TEST_PROPFIND5 ----------------- // test duplicate check WebdavPropfindRequest *p5 = propfind_parse(sn, rq, TEST_PROPFIND5, strlen(TEST_PROPFIND5), &error); CX_TEST_ASSERT(p5); CX_TEST_ASSERT(p5->properties); CX_TEST_ASSERT(!p5->allprop); CX_TEST_ASSERT(!p5->propname); CX_TEST_ASSERT(p5->propcount == 4); // property 1: DAV:displayname elm = p5->properties; CX_TEST_ASSERT(elm); CX_TEST_ASSERT( !strcmp(elm->property->name, "displayname")); CX_TEST_ASSERT( !strcmp((char*)elm->property->namespace->href, "DAV:")); elm = elm->next; CX_TEST_ASSERT(elm); elm = elm->next; CX_TEST_ASSERT(elm); // property 4: DAV:resourcetype elm = elm->next; CX_TEST_ASSERT(elm); CX_TEST_ASSERT( !strcmp(elm->property->name, "resourcetype")); CX_TEST_ASSERT( !strcmp((char*)elm->property->namespace->href, "DAV:")); // // ----------------- TEST_PROPFIND6 ----------------- // test prop/allprop mix WebdavPropfindRequest *p6 = propfind_parse(sn, rq, TEST_PROPFIND6, strlen(TEST_PROPFIND6), &error); CX_TEST_ASSERT(p6); CX_TEST_ASSERT(!p6->properties); CX_TEST_ASSERT(p6->allprop); CX_TEST_ASSERT(!p6->propname); CX_TEST_ASSERT(p6->propcount == 0); } pool_destroy(sn->pool); } CX_TEST(test_proppatch_parse) { Session *sn = testutil_session(); Request *rq = testutil_request(sn->pool, "PROPPATCH", "/"); CX_TEST_DO { int error = 0; WebdavProppatchRequest *p1 = proppatch_parse(sn, rq, TEST_PROPPATCH1, strlen(TEST_PROPPATCH1), &error); CX_TEST_ASSERT(p1->set); CX_TEST_ASSERT(!p1->remove); CX_TEST_ASSERT(p1->setcount == 2); CX_TEST_ASSERT(p1->set->next); CX_TEST_ASSERT(!p1->set->next->next); CX_TEST_ASSERT(p1->set->property); CX_TEST_ASSERT( !strcmp(p1->set->property->name, "test")); WebdavProppatchRequest *p2 = proppatch_parse(sn, rq, TEST_PROPPATCH2, strlen(TEST_PROPPATCH2), &error); CX_TEST_ASSERT(p2->set); CX_TEST_ASSERT(p2->remove); CX_TEST_ASSERT(p2->setcount == 4); CX_TEST_ASSERT(p2->removecount == 1); CX_TEST_ASSERT( !strcmp((char*)p2->set->property->namespace->href, "http://example.com/")); CX_TEST_ASSERT( !strcmp(p2->set->property->name, "a")); WSXmlNode *p2set1 = p2->set->property->value.node; CX_TEST_ASSERT(p2set1->type == WS_NODE_TEXT); CX_TEST_ASSERT(p2set1->content); CX_TEST_ASSERT(!strcmp((char*)p2set1->content, "test")); WSXmlNode *p2set3 = p2->set->next->next->property->value.node; CX_TEST_ASSERT(p2set3); CX_TEST_ASSERT(p2set3->type == WS_NODE_TEXT); CX_TEST_ASSERT(p2set3->next); CX_TEST_ASSERT(xmlHasProp(p2set3->next, BAD_CAST"test")); CX_TEST_ASSERT(xmlHasProp(p2set3->next, BAD_CAST"abc")); xmlChar *value1 = xmlGetProp(p2set3->next, BAD_CAST"test"); CX_TEST_ASSERT(!strcmp((char*) value1, "test1")); xmlFree(value1); xmlChar *value2 = xmlGetProp(p2set3->next, BAD_CAST"abc"); CX_TEST_ASSERT(!strcmp((char*) value2, "def")); xmlFree(value2); CX_TEST_ASSERT(!strcmp(p2->remove->property->name, "e")); } pool_destroy(sn->pool); } CX_TEST(test_lock_parse) { Session *sn = testutil_session(); Request *rq = testutil_request(sn->pool, "LOCK", "/"); CX_TEST_DO { int error = 0; WebdavLockRequest *l1 = lock_parse(sn, rq, TEST_LOCK1, strlen(TEST_LOCK1), &error); CX_TEST_ASSERT(l1); CX_TEST_ASSERT(l1->type == WEBDAV_LOCK_WRITE); CX_TEST_ASSERT(l1->scope == WEBDAV_LOCK_SHARED); CX_TEST_ASSERT(l1->owner); CX_TEST_ASSERT(!strcmp((char*)l1->owner->content, "User")); } pool_destroy(sn->pool); } CX_TEST(test_rqbody2buffer) { Session *sn; Request *rq; CX_TEST_DO { // // TEST 1 sn = testutil_session(); rq = testutil_request(sn->pool, "PUT", "/"); testutil_request_body(sn, rq, "Hello World!", 12); CxBuffer b1; rqbody2buffer(sn, rq, &b1); CX_TEST_ASSERT(b1.size == 12); CX_TEST_ASSERT(!memcmp(b1.space,"Hello World!",12)); cxBufferDestroy(&b1); testutil_destroy_session(sn); // // TEST 2 size_t len1 = 25000; unsigned char *body1 = malloc(len1); for(int i=0;i<len1;i++) { body1[i] = i; } sn = testutil_session(); rq = testutil_request(sn->pool, "PUT", "/"); testutil_request_body(sn, rq, (char*)body1, len1); CxBuffer b2; rqbody2buffer(sn, rq, &b2); CX_TEST_ASSERT(b2.size == len1); CX_TEST_ASSERT(!memcmp(b2.space, body1, len1)); cxBufferDestroy(&b2); testutil_destroy_session(sn); } } CX_TEST(test_webdav_plist_iterator) { Session *sn; Request *rq; WebdavPropfindRequest *propfind; CX_TEST_DO { CX_TEST_ASSERT(!test_init(&sn, &rq, &propfind, TEST_PROPFIND1)); WebdavPList *properties = propfind->properties; size_t count = 0; WebdavPListIterator i = webdav_plist_iterator(&properties); WebdavPList *cur; while(webdav_plist_iterator_next(&i, &cur)) { switch(i.index) { case 0: { CX_TEST_ASSERT(!strcmp(cur->property->name, "displayname")); break; } case 1: { CX_TEST_ASSERT(!strcmp(cur->property->name, "getcontentlength")); break; } case 2: { CX_TEST_ASSERT(!strcmp(cur->property->name, "getcontenttype")); break; } case 3: { CX_TEST_ASSERT(!strcmp(cur->property->name, "getlastmodified")); break; } case 4: { CX_TEST_ASSERT(!strcmp(cur->property->name, "resourcetype")); break; } case 5: { CX_TEST_ASSERT(!strcmp(cur->property->name, "getetag")); break; } } count++; } CX_TEST_ASSERT(count == propfind->propcount); } testutil_destroy_session(sn); } CX_TEST(test_webdav_plist_iterator_remove_current) { Session *sn; Request *rq; WebdavPropfindRequest *propfind; CX_TEST_DO { CX_TEST_ASSERT(!test_init(&sn, &rq, &propfind, TEST_PROPFIND1)); WebdavPList *properties1 = webdav_plist_clone(sn->pool, propfind->properties); WebdavPList *properties2 = webdav_plist_clone(sn->pool, propfind->properties); WebdavPList *properties3 = webdav_plist_clone(sn->pool, propfind->properties); WebdavPList *properties4 = webdav_plist_clone(sn->pool, propfind->properties); WebdavPListIterator i; WebdavPList *cur; // test removal of first element i = webdav_plist_iterator(&properties1); while(webdav_plist_iterator_next(&i, &cur)) { if(i.index == 0) { webdav_plist_iterator_remove_current(&i); } } CX_TEST_ASSERT(!properties1->prev); CX_TEST_ASSERT(!strcmp(properties1->property->name, "getcontentlength")); CX_TEST_ASSERT(!strcmp(properties1->next->property->name, "getcontenttype")); CX_TEST_ASSERT(properties1->next->prev == properties1); // test removal of second element i = webdav_plist_iterator(&properties2); while(webdav_plist_iterator_next(&i, &cur)) { if(i.index == 1) { webdav_plist_iterator_remove_current(&i); } } CX_TEST_ASSERT(!strcmp(properties2->next->property->name, "getcontenttype")); CX_TEST_ASSERT(properties2->next->prev == properties2); CX_TEST_ASSERT(webdav_plist_size(properties2) == 5); // remove last element i = webdav_plist_iterator(&properties3); while(webdav_plist_iterator_next(&i, &cur)) { if(i.index == 5) { webdav_plist_iterator_remove_current(&i); } } CX_TEST_ASSERT(webdav_plist_size(properties3) == 5); CX_TEST_ASSERT(!strcmp(properties3->next->next->next->next->property->name, "resourcetype")); // remove all elements i = webdav_plist_iterator(&properties4); while(webdav_plist_iterator_next(&i, &cur)) { webdav_plist_iterator_remove_current(&i); switch(i.index) { case 0: { CX_TEST_ASSERT(!strcmp(properties4->property->name, "getcontentlength")); CX_TEST_ASSERT(properties4->prev == NULL); break; } case 1: { CX_TEST_ASSERT(!strcmp(properties4->property->name, "getcontenttype")); CX_TEST_ASSERT(properties4->prev == NULL); break; } case 2: { CX_TEST_ASSERT(!strcmp(properties4->property->name, "getlastmodified")); CX_TEST_ASSERT(properties4->prev == NULL); break; } case 3: { CX_TEST_ASSERT(!strcmp(properties4->property->name, "resourcetype")); CX_TEST_ASSERT(properties4->prev == NULL); break; } case 4: { CX_TEST_ASSERT(!strcmp(properties4->property->name, "getetag")); CX_TEST_ASSERT(properties4->prev == NULL); break; } default: { CX_TEST_ASSERT(i.index <= 5); } } } CX_TEST_ASSERT(properties4 == NULL); } testutil_destroy_session(sn); } CX_TEST(test_msresponse_addproperty) { Session *sn; Request *rq; CX_TEST_DO { WebdavOperation *op = test_propfind_op(&sn, &rq, TEST_PROPFIND1); CX_TEST_ASSERT(op); CX_TEST_ASSERT(op->response); Multistatus *ms = (Multistatus*)op->response; MSResponse *r = (MSResponse*)ms->response.addresource((WebdavResponse*)ms, "/"); WebdavProperty p1; WebdavProperty p[16]; const char *names[] = {"a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9"}; WSNamespace ns1; ZERO(&ns1, sizeof(WSNamespace)); WSNamespace ns2; ZERO(&ns2, sizeof(WSNamespace)); ns1.prefix = (xmlChar*)"x1"; ns1.href = (xmlChar*)"http://example.com/test/"; ns2.prefix = (xmlChar*)"x2"; ns2.href = (xmlChar*)"http://example.com/test/"; WebdavProperty dp1; ZERO(&dp1, sizeof(WebdavProperty)); dp1.name = "dup"; dp1.namespace = &ns1; dp1.value.text.str = "Hello"; dp1.value.text.length = 5; dp1.vtype = WS_VALUE_TEXT; WebdavProperty dp2; ZERO(&dp2, sizeof(WebdavProperty)); dp2.name = "dup"; dp2.namespace = &ns1; dp2.value.text.str = "Hello"; dp2.value.text.length = 5; dp2.vtype = WS_VALUE_TEXT; WebdavProperty dp3; ZERO(&dp3, sizeof(WebdavProperty)); dp3.name = "dup"; dp3.namespace = &ns2; dp3.value.text.str = "Hello"; dp3.value.text.length = 5; dp3.vtype = WS_VALUE_TEXT; // init test data p1.namespace = webdav_dav_namespace(); p1.lang = NULL; p1.name = "test1"; p1.value.data = (WSXmlData){ NULL, NULL, 0}; p1.vtype = 0; for(int i=0;i<8;i++) { p[i].namespace = webdav_dav_namespace(); p[i].name = names[i]; p[i].lang = NULL; p[i].value.node = NULL; p[1].vtype = 0; } CX_TEST_ASSERT(!r->plist_begin && !r->plist_end); r->resource.addproperty((WebdavResource*)r, &p1, 200); CX_TEST_ASSERT(r->plist_begin); CX_TEST_ASSERT(r->plist_begin == r->plist_end); r->resource.addproperty((WebdavResource*)r, &p[0], 404); r->resource.addproperty((WebdavResource*)r, &p[1], 404); r->resource.addproperty((WebdavResource*)r, &p[2], 403); r->resource.addproperty((WebdavResource*)r, &p[3], 403); r->resource.addproperty((WebdavResource*)r, &p[4], 403); r->resource.addproperty((WebdavResource*)r, &p[5], 403); r->resource.addproperty((WebdavResource*)r, &p[6], 500); CX_TEST_ASSERT(r->plist_begin == r->plist_end); CX_TEST_ASSERT(r->errors); CX_TEST_ASSERT(r->errors->next); CX_TEST_ASSERT(r->errors->next->next); CX_TEST_ASSERT(!r->errors->next->next->next); CX_TEST_ASSERT(webdav_plist_size(r->errors->begin) == 2); CX_TEST_ASSERT(webdav_plist_size(r->errors->next->begin) == 4); CX_TEST_ASSERT(webdav_plist_size(r->errors->next->next->begin) == 1); // new resource for prop duplication tests r = (MSResponse*)ms->response.addresource((WebdavResponse*)ms, "/test"); CX_TEST_ASSERT(r); r->resource.addproperty((WebdavResource*)r, &dp1, 200); CX_TEST_ASSERT(r->plist_begin); CX_TEST_ASSERT(!r->plist_begin->next); r->resource.addproperty((WebdavResource*)r, &dp2, 200); CX_TEST_ASSERT(!r->plist_begin->next); r->resource.addproperty((WebdavResource*)r, &dp2, 404); CX_TEST_ASSERT(!r->plist_begin->next); if(r->errors) { CX_TEST_ASSERT(webdav_plist_size(r->errors->begin) == 0); } r->resource.addproperty((WebdavResource*)r, &dp3, 200); CX_TEST_ASSERT(!r->plist_begin->next); } } CX_TEST(test_webdav_propfind_init) { reset_backends(); Session *sn; Request *rq; WebdavPropfindRequest *propfind; CX_TEST_DO { CX_TEST_ASSERT(!test_init(&sn, &rq, &propfind, TEST_PROPFIND1)); WebdavPropfindRequestList *requests = NULL; int err = webdav_propfind_init(&backend1, propfind, "/", "/", &requests); CX_TEST_ASSERT(!err); CX_TEST_ASSERT(requests); CX_TEST_ASSERT(cx_linked_list_size(requests, offsetof(WebdavPropfindRequestList, next))); WebdavPropfindRequest *p1 = requests->propfind; WebdavPropfindRequest *p2 = requests->next->propfind; // backend1 removes the first property from the plist // backend2 should have one property less CX_TEST_ASSERT(p1 && p2); CX_TEST_ASSERT(p1 != p2); CX_TEST_ASSERT(p1->properties != p2->properties); CX_TEST_ASSERT(p1->propcount == p2->propcount + 1); CX_TEST_ASSERT(backend1_init_called == 1); CX_TEST_ASSERT(backend2_init_called == 1); } pool_destroy(sn->pool); } CX_TEST(test_webdav_op_propfind_begin) { reset_backends(); Session *sn; Request *rq; CX_TEST_DO { WebdavOperation *op = test_propfind_op(&sn, &rq, TEST_PROPFIND1); CX_TEST_ASSERT(op); int err = webdav_op_propfind_begin(op, "/", NULL, NULL); CX_TEST_ASSERT(err == 0); CX_TEST_ASSERT(backend1_propfind_do_count == 1); CX_TEST_ASSERT(backend2_propfind_do_count == 1); } testutil_destroy_session(sn); } CX_TEST(test_webdav_op_propfind_children) { reset_backends(); Session *sn; Request *rq; CX_TEST_DO { WebdavOperation *op = test_propfind_op(&sn, &rq, TEST_PROPFIND1); CX_TEST_ASSERT(op); int err = webdav_op_propfind_begin(op, "/", NULL, NULL); CX_TEST_ASSERT(err == 0); // create test vfs with some files (code from test_vfs_readdir) rq->vfs = testvfs_create(sn); VFSContext *vfs = vfs_request_context(sn, rq); CX_TEST_ASSERT(vfs); err = vfs_mkdir(vfs, "/dir"); CX_TEST_ASSERT(err == 0); // add some test file to /dir CX_TEST_ASSERT(vfs_open(vfs, "/dir/file1", O_CREAT)); CX_TEST_ASSERT(vfs_open(vfs, "/dir/file2", O_CREAT)); CX_TEST_ASSERT(vfs_open(vfs, "/dir/file3", O_CREAT)); CX_TEST_ASSERT(vfs_open(vfs, "/dir/file4", O_CREAT)); VFSDir *dir = vfs_opendir(vfs, "/dir"); CX_TEST_ASSERT(dir); CX_TEST_ASSERT(backend1_propfind_do_count == 1); CX_TEST_ASSERT(backend2_propfind_do_count == 1); // propfind for all children err = webdav_op_propfind_children(op, vfs, "/", "/dir"); CX_TEST_ASSERT(err == 0); // 1 dir + 4 children CX_TEST_ASSERT(backend1_propfind_do_count == 5); CX_TEST_ASSERT(backend2_propfind_do_count == 5); } testutil_destroy_session(sn); } void init_test_webdav_method( Session **out_sn, Request **out_rq, TestIOStream **out_st, pblock **out_pb, const char *method, const char *path, const char *request_body) { Session *sn; Request *rq; TestIOStream *st; pblock *pb; sn = testutil_session(); rq = testutil_request(sn->pool, method, "/"); pblock_nvinsert("path", path, rq->vars); pblock_nvinsert("uri", path, rq->reqpb); st = testutil_iostream(2048, TRUE); sn->csd = (IOStream*)st; if(request_body) { testutil_request_body(sn, rq, request_body, strlen(request_body)); } pb = pblock_create_pool(sn->pool, 4); *out_sn = sn; *out_rq = rq; *out_st = st; *out_pb = pb; } CX_TEST(test_webdav_propfind) { Session *sn; Request *rq; TestIOStream *st; pblock *pb; CX_TEST_DO { int ret; // Test 1 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/", TEST_PROPFIND1); ret = webdav_propfind(pb, sn, rq); CX_TEST_ASSERT(ret == REQ_PROCEED); xmlDoc *doc = xmlReadMemory( st->buf->space, st->buf->size, NULL, NULL, 0); CX_TEST_ASSERT(doc); //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); testutil_destroy_session(sn); xmlFreeDoc(doc); testutil_iostream_destroy(st); // Test2 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/", TEST_PROPFIND2); ret = webdav_propfind(pb, sn, rq); CX_TEST_ASSERT(ret == REQ_PROCEED); xmlDoc *doc2 = xmlReadMemory( st->buf->space, st->buf->size, NULL, NULL, 0); CX_TEST_ASSERT(doc); //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); testutil_destroy_session(sn); xmlFreeDoc(doc2); testutil_iostream_destroy(st); } } /* ------------------------------------------------------------------------- * * PROPPATCH TESTS * * ------------------------------------------------------------------------ */ static int test_proppatch_init( Session **out_sn, Request **out_rq, WebdavProppatchRequest **out_proppatch, const char *xml) { if(!webdav_is_initialized) { if(webdav_init(NULL, NULL, NULL) != REQ_PROCEED) { return 1; } webdav_is_initialized = 1; } Session *sn = testutil_session(); Request *rq = testutil_request(sn->pool, "PROPPATCH", "/"); int error = 0; WebdavProppatchRequest *proppatch = proppatch_parse( sn, rq, xml, strlen(xml), &error); if(error) { return 1; } if(!proppatch || !(proppatch->set || proppatch->remove)) { return 1; } *out_sn = sn; *out_rq = rq; *out_proppatch = proppatch; return 0; } static WebdavOperation* test_proppatch_op1( Session **out_sn, Request **out_rq, const char *xml) { WebdavProppatchRequest *proppatch; if(test_proppatch_init(out_sn, out_rq, &proppatch, xml)) { return NULL; } Multistatus *ms = multistatus_response(*out_sn, *out_rq); if(!ms) { return NULL; } // WebdavResponse is the public interface used by Backends // for adding resources to the response WebdavResponse *response = (WebdavResponse*)ms; return webdav_create_proppatch_operation( (*out_sn), (*out_rq), &backend1, proppatch, response); } CX_TEST(test_proppatch_msresponse) { Session *sn; Request *rq; WebdavOperation *op; Multistatus *ms; WebdavResource *res; WebdavProperty p[16]; const char *names[] = {"a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9"}; for(int i=0;i<8;i++) { p[i].namespace = webdav_dav_namespace(); p[i].name = names[i]; p[i].lang = NULL; p[i].value.node = NULL; p[i].vtype = 0; } CX_TEST_DO { op = test_proppatch_op1(&sn, &rq, TEST_PROPPATCH2); CX_TEST_ASSERT(op); ms = (Multistatus*)op->response; ms->proppatch = TRUE; res = ms->response.addresource(&ms->response, "/"); CX_TEST_ASSERT(res); CX_TEST_ASSERT(!res->addproperty(res, &p[0], 200)); CX_TEST_ASSERT(!res->addproperty(res, &p[1], 200)); CX_TEST_ASSERT(!res->addproperty(res, &p[2], 200)); CX_TEST_ASSERT(!res->addproperty(res, &p[3], 200)); CX_TEST_ASSERT(!res->close(res)); MSResponse *msres = (MSResponse*)res; CX_TEST_ASSERT(!msres->errors); CX_TEST_ASSERT(msres->plist_begin); CX_TEST_ASSERT(msres->plist_begin->next); CX_TEST_ASSERT(msres->plist_begin->next->next); CX_TEST_ASSERT(msres->plist_begin->next->next->next); CX_TEST_ASSERT(!msres->plist_begin->next->next->next->next); } testutil_destroy_session(sn); } CX_TEST(test_msresponse_addproperty_with_errors) { Session *sn; Request *rq; WebdavOperation *op; Multistatus *ms; WebdavResource *res; WebdavProperty p[16]; const char *names[] = {"a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9"}; for(int i=0;i<8;i++) { p[i].namespace = webdav_dav_namespace(); p[i].name = names[i]; p[i].lang = NULL; p[i].value.node = NULL; p[i].vtype = 0; } CX_TEST_DO { op = test_proppatch_op1(&sn, &rq, TEST_PROPPATCH2); CX_TEST_ASSERT(op); ms = (Multistatus*)op->response; ms->proppatch = TRUE; res = ms->response.addresource(&ms->response, "/"); CX_TEST_ASSERT(res); CX_TEST_ASSERT(!res->addproperty(res, &p[0], 200)); CX_TEST_ASSERT(!res->addproperty(res, &p[1], 200)); CX_TEST_ASSERT(!res->addproperty(res, &p[2], 409)); CX_TEST_ASSERT(!res->addproperty(res, &p[3], 200)); CX_TEST_ASSERT(!res->close(res)); // all properties should have an error status code now // 1 x 409, 3 x 424 MSResponse *msres = (MSResponse*)res; CX_TEST_ASSERT(!msres->plist_begin); CX_TEST_ASSERT(msres->errors); CX_TEST_ASSERT(msres->errors->next); CX_TEST_ASSERT(!msres->errors->next->next); // We know that we have 2 error lists, one with status code 409 and // the other must have 409. However we don't enforce the order of the // error lists, therefore check both variants if(msres->errors->status == 409) { CX_TEST_ASSERT(msres->errors->next->status == 424); CX_TEST_ASSERT(msres->errors->begin); CX_TEST_ASSERT(msres->errors->next->begin); } else { CX_TEST_ASSERT(msres->errors->next->status == 409); CX_TEST_ASSERT(msres->errors->begin); CX_TEST_ASSERT(msres->errors->next->begin); } } testutil_destroy_session(sn); } CX_TEST(test_webdav_op_proppatch) { Session *sn; Request *rq; WebdavOperation *op; Multistatus *ms; WebdavResource *res; WebdavProperty p[16]; const char *names[] = {"a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9"}; for(int i=0;i<8;i++) { p[i].namespace = webdav_dav_namespace(); p[i].name = names[i]; p[i].lang = NULL; p[i].value.node = NULL; p[1].vtype = 0; } CX_TEST_DO { // TEST_PROPPATCH2 should succeed reset_backends(); op = test_proppatch_op1(&sn, &rq, TEST_PROPPATCH2); CX_TEST_ASSERT(op); int ret = webdav_op_proppatch(op, "/", "/"); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(backend1_proppatch_commit); CX_TEST_ASSERT(backend2_proppatch_commit); CX_TEST_ASSERT(backend1_proppatch_do_count == 1); CX_TEST_ASSERT(backend2_proppatch_do_count == 1); CX_TEST_ASSERT(backend1_proppatch_finish_count == 1); CX_TEST_ASSERT(backend2_proppatch_finish_count == 1); // TEST_PROPPATCH3 should fail (commit == FALSE) reset_backends(); op = test_proppatch_op1(&sn, &rq, TEST_PROPPATCH3); CX_TEST_ASSERT(op); ret = webdav_op_proppatch(op, "/", "/"); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(!backend1_proppatch_commit); CX_TEST_ASSERT(!backend2_proppatch_commit); // TEST_PROPPATCH4 should abort reset_backends(); op = test_proppatch_op1(&sn, &rq, TEST_PROPPATCH4); CX_TEST_ASSERT(op); ret = webdav_op_proppatch(op, "/", "/"); CX_TEST_ASSERT(ret != 0); CX_TEST_ASSERT(backend1_proppatch_do_count == 1); CX_TEST_ASSERT(backend2_proppatch_do_count == 1); CX_TEST_ASSERT(backend1_proppatch_finish_count == 1); CX_TEST_ASSERT(backend2_proppatch_finish_count == 0); } testutil_destroy_session(sn); } #define xstreq(a, b) (!strcmp((const char*)a, (const char*)b)) CX_TEST(test_webdav_proppatch) { Session *sn; Request *rq; TestIOStream *st; pblock *pb; CX_TEST_DO { int ret; // Test 1 init_test_webdav_method(&sn, &rq, &st, &pb, "PROPPATCH", "/", TEST_PROPPATCH2); rq->davCollection = &backend1; ret = webdav_proppatch(pb, sn, rq); CX_TEST_ASSERT(ret == REQ_PROCEED); xmlDoc *doc = xmlReadMemory( st->buf->space, st->buf->size, NULL, NULL, 0); CX_TEST_ASSERT(doc); //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space); xmlNode *root = xmlDocGetRootElement(doc); CX_TEST_ASSERT(root); xmlNode *nodeC = NULL; xmlNode *node = root->children; int depth = 1; while(node) { const xmlChar *name = node->name; int nextNode = 1; if(node->type != XML_ELEMENT_NODE) { // nothing } else if(depth == 1) { if(xstreq(name, "response")) { nextNode = 0; } } else if(depth == 2) { if(xstreq(name, "propstat")) { nextNode = 0; } } else if(depth == 3) { if(xstreq(name, "prop")) { nextNode = 0; } } else if(depth == 4) { if(xstreq(name, "c")) { nodeC = node; break; } } if(nextNode) { node = node->next; } else { node = node->children; depth++; } } CX_TEST_ASSERT(nodeC); CX_TEST_ASSERT(!nodeC->children); testutil_destroy_session(sn); xmlFreeDoc(doc); testutil_iostream_destroy(st); } } /* ------------------------------------------------------------------------- * * WEBDAV VFS TESTS * * ------------------------------------------------------------------------ */ static int mkcol_data1 = 10; static int mkcol_data2 = 20; static int mkcol_data3 = 30; static int mkcol_data4 = 40; static int mkcol_count = 0; static int mkcol_finish_count = 0; static int mkcol_err = 0; static int set_created = 0; static int test_webdav_mkcol(WebdavVFSRequest *req, WSBool *created) { mkcol_count++; switch(mkcol_count) { case 1: { req->userdata = &mkcol_data1; break; } case 2: { req->userdata = &mkcol_data2; break; } case 3: { req->userdata = &mkcol_data3; break; } case 4: { req->userdata = &mkcol_data4; break; } default: break; } if(set_created) { *created = TRUE; set_created = 0; } return 0; } static int test_webdav_mkcol_finish(WebdavVFSRequest *req, WSBool success) { mkcol_finish_count++; if(mkcol_finish_count == 1) { int *data = req->userdata; if(data != &mkcol_data1) { mkcol_err = 1; } } else if(mkcol_finish_count == 3) { int *data = req->userdata; if(data != &mkcol_data3) { mkcol_err = 1; } } else { int *data = req->userdata; // data4 should never be used if(data == &mkcol_data4) { mkcol_err = 1; } } return 0; } static int test_webdav_mkcol_fail(WebdavVFSRequest *req, WSBool *created) { mkcol_count++; return 1; } static int delete_count = 0; static int delete_finish_count = 0; static int test_backend_webdav_delete(WebdavVFSRequest *req, WSBool *created) { delete_count++; return 0; } static int test_backend_webdav_delete_finish(WebdavVFSRequest *req, WSBool success) { delete_finish_count++; return 0; } CX_TEST(test_webdav_vfs_op_do) { Session *sn; Request *rq; TestIOStream *st; pblock *pb; // Tests performed primarily with MKCOL, because webdav_vfs_op_do // behaves the same for both operations // the only difference are the callbacks init_test_webdav_method(&sn, &rq, &st, &pb, "MKCOL", "/", NULL); VFS *testvfs = testvfs_create(sn); rq->vfs = testvfs; WebdavBackend dav1; ZERO(&dav1, sizeof(WebdavBackend)); dav1.opt_mkcol = test_webdav_mkcol; dav1.opt_mkcol_finish = test_webdav_mkcol_finish; dav1.opt_delete = test_backend_webdav_delete; dav1.opt_delete_finish = test_backend_webdav_delete_finish; WebdavBackend dav2; ZERO(&dav2, sizeof(WebdavBackend)); dav2.opt_mkcol_finish = test_webdav_mkcol_finish; WebdavBackend dav3; ZERO(&dav3, sizeof(WebdavBackend)); dav3.opt_mkcol = test_webdav_mkcol; WebdavBackend dav4; ZERO(&dav4, sizeof(WebdavBackend)); dav4.opt_mkcol = test_webdav_mkcol; dav4.opt_mkcol_finish = test_webdav_mkcol_finish; dav1.next = &dav2; dav2.next = &dav3; dav3.next = &dav4; rq->davCollection = &dav1; CX_TEST_DO { WebdavVFSOperation *op1 = webdav_vfs_op(sn, rq, &dav1, FALSE); int ret = webdav_vfs_op_do(op1, WEBDAV_VFS_MKDIR); CX_TEST_ASSERT(!ret); CX_TEST_ASSERT(mkcol_count == 3); CX_TEST_ASSERT(mkcol_finish_count == 3); CX_TEST_ASSERT(mkcol_err == 0); // test without VFS, but set *created to TRUE to skip VFS usage rq->vfs = NULL; set_created = 1; WebdavVFSOperation *op2 = webdav_vfs_op(sn, rq, &dav1, FALSE); ret = webdav_vfs_op_do(op2, WEBDAV_VFS_MKDIR); CX_TEST_ASSERT(!ret); // test 3: abort after first backend mkcol_count = 0; mkcol_finish_count = 0; dav1.opt_mkcol = test_webdav_mkcol_fail; WebdavVFSOperation *op3 = webdav_vfs_op(sn, rq, &dav1, FALSE); ret = webdav_vfs_op_do(op3, WEBDAV_VFS_MKDIR); CX_TEST_ASSERT(ret); CX_TEST_ASSERT(mkcol_count == 1); CX_TEST_ASSERT(mkcol_finish_count == 1); // test DELETE to make sure, delete callbacks will be used pblock_replace("path", "/deltest", rq->vars); rq->vfs = testvfs; WebdavVFSOperation *op_del = webdav_vfs_op(sn, rq, &dav1, FALSE); vfs_open(op_del->vfs, "/deltest", O_CREAT); ret = webdav_vfs_op_do(op_del, WEBDAV_VFS_DELETE); CX_TEST_ASSERT(!ret); CX_TEST_ASSERT(delete_count == 1); CX_TEST_ASSERT(delete_finish_count == 1); } } CX_TEST(test_webdav_delete){ Session *sn; Request *rq; TestIOStream *st; pblock *pb; init_test_webdav_method(&sn, &rq, &st, &pb, "DELETE", "/", NULL); rq->vfs = testvfs_create(sn); WebdavBackend dav1; ZERO(&dav1, sizeof(WebdavBackend)); dav1.opt_delete = test_backend_webdav_delete; dav1.opt_delete_finish = test_backend_webdav_delete_finish; delete_count = 0; delete_finish_count = 0; rq->davCollection = &dav1; CX_TEST_DO { // prepare VFSContext *vfs = vfs_request_context(sn, rq); int err; err = vfs_mkdir(vfs, "/dir1"); CX_TEST_ASSERT(err == 0); err = vfs_mkdir(vfs, "/dir2"); CX_TEST_ASSERT(err == 0); err = vfs_mkdir(vfs, "/dir2/dir3"); CX_TEST_ASSERT(err == 0); err = vfs_mkdir(vfs, "/dir2/dir4"); CX_TEST_ASSERT(err == 0); err = vfs_mkdir(vfs, "/dir2/dir4/dir5"); CX_TEST_ASSERT(err == 0); SYS_FILE f0 = vfs_open(vfs, "/file0", O_CREAT); CX_TEST_ASSERT(f0); // no f1 SYS_FILE f2 = vfs_open(vfs, "/dir2/file2", O_CREAT); CX_TEST_ASSERT(f2); SYS_FILE f3 = vfs_open(vfs, "/dir2/dir3/file3", O_CREAT); CX_TEST_ASSERT(f3); SYS_FILE f4 = vfs_open(vfs, "/dir2/dir4/file4", O_CREAT); CX_TEST_ASSERT(f4); SYS_FILE f5 = vfs_open(vfs, "/dir2/dir4/dir5/file5", O_CREAT); CX_TEST_ASSERT(f5); // delete single file pblock_replace("path", "/file0", rq->vars); err = webdav_delete(NULL, sn, rq); CX_TEST_ASSERT(err == 0); CX_TEST_ASSERT(delete_count == 1); delete_count = 0; pblock_replace("path", "/dir1", rq->vars); err = webdav_delete(NULL, sn, rq); CX_TEST_ASSERT(err == 0); CX_TEST_ASSERT(delete_count == 1); delete_count = 0; pblock_replace("path", "/dir2", rq->vars); err = webdav_delete(NULL, sn, rq); CX_TEST_ASSERT(err == 0); CX_TEST_ASSERT(delete_count == 8); } } CX_TEST(test_webdav_put) { Session *sn; Request *rq; TestIOStream *st; pblock *pb; const char *content_const = "Hello World"; init_test_webdav_method(&sn, &rq, &st, &pb, "PUT", "/", content_const); rq->vfs = testvfs_create(sn); CX_TEST_DO { int err; pblock_replace("path", "/file0", rq->vars); err = webdav_put(NULL, sn, rq); CX_TEST_ASSERT(err == REQ_PROCEED); VFSContext *vfs = vfs_request_context(sn, rq); SYS_FILE f0 = vfs_open(vfs, "/file0", 0); CX_TEST_ASSERT(f0); char buf[1024]; int r = system_fread(f0, buf, 1024); CX_TEST_ASSERT(r == strlen(content_const)); CX_TEST_ASSERT(!memcmp(content_const, buf, r)); testutil_destroy_session(sn); testutil_iostream_destroy(st); } }