Sat, 24 Jan 2026 13:56:19 +0100
integrate wsgidav into test suite
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2026 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 "wsgidav.h" #include <stdio.h> #include <stdlib.h> //ifndef _WIN32 #include <unistd.h> #include <sys/fcntl.h> #include <spawn.h> #include <sys/wait.h> #include <sys/stat.h> #include <cx/string.h> #include <cx/buffer.h> #include <cx/streams.h> #include <cx/printf.h> // -------------------------- Process Utils -------------------------------- typedef struct { pid_t pid; int in; int out; int err; } Process; static Process process_spawn(const char *exec, char **args) { Process proc = { 0 }; int pin[2]; int pout[2]; int perr[2]; // child process stdin/stdout pipes if(pipe(pin)) { perror("pipe"); return proc; } if(pipe(pout)) { perror("pipe"); close(pin[0]); close(pin[1]); } if(pipe(perr)) { perror("pipe"); close(pin[0]); close(pin[1]); close(pout[0]); close(pout[1]); } posix_spawn_file_actions_t actions; posix_spawn_file_actions_init(&actions); posix_spawn_file_actions_adddup2(&actions, pin[0], 0); posix_spawn_file_actions_adddup2(&actions, pout[1], 1); posix_spawn_file_actions_adddup2(&actions, perr[1], 2); // child environment variables char *path = getenv("PATH"); if(!path) { path = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin"; } cxmutstr env_path = cx_asprintf("PATH=%s", path); char *env[10]; env[0] = "LC_ALL=C"; env[1] = env_path.ptr; env[2] = NULL; // array must be null-terminated // execute posix_spawn to create a child process pid_t child; int ret = posix_spawn( &child, exec, &actions, NULL, // attributes args, env); posix_spawn_file_actions_destroy(&actions); close(pin[0]); close(pout[1]); close(perr[1]); if(ret) { // posix spawn failed perror("posix_spawn"); // close remaining pipe fds close(pin[1]); close(pout[0]); close(perr[0]); } else { proc.pid = child; proc.in = pin[1]; proc.out = pout[0]; proc.err = perr[0]; } return proc; } int process_close(Process *p) { if(p->pid == 0) { return -1; } close(p->in); close(p->out); int status = -1; waitpid(p->pid, &status, 0); return status; } size_t process_read(void *buf, size_t size, size_t n, Process *p) { ssize_t r = read(p->out, buf, size*n); return r > 0 ? r : 0; } size_t process_err_read(void *buf, size_t size, size_t n, Process *p) { ssize_t r = read(p->err, buf, size*n); return r > 0 ? r : 0; } static cxmutstr read_str(Process *p, cx_read_func readf) { if(p->pid == 0) { return CX_NULLSTR; } CxBuffer buffer; cxBufferInit(&buffer, NULL, NULL, 1024, CX_BUFFER_AUTO_EXTEND); cx_stream_copy(p, &buffer, readf, (cx_write_func)cxBufferWrite); cxBufferTerminate(&buffer); return cx_mutstrn(buffer.space, buffer.size); } cxmutstr process_read_string(Process *p) { return read_str(p, (cx_read_func)process_read); } cxmutstr process_err_read_string(Process *p) { return read_str(p, (cx_read_func)process_err_read); } // ------------------------------ wsgidav exec ----------------------------- static char *wsgidav_exec_path; static Process wsgidav_process; static int runtest_root_dir = 0; int wsgidav_is_available(void) { // check if the wsgidava binary is available char *args[] = { "which", "wsgidav", NULL }; Process p = process_spawn("/usr/bin/which", args); if(p.pid == 0) { return 0; } cxmutstr path = process_read_string(&p); int status = process_close(&p); if(status == 0) { wsgidav_exec_path = cx_strdup(cx_strtrim(path)).ptr; } free(path.ptr); if(status != 0) { return 0; } // check if the wsgidav config file is available struct stat s; if(!stat("test/wsgidav/wsgidav.yaml", &s)) { runtest_root_dir = 1; // dav root directory } else { if(!stat("../test/wsgidav/wsgidav.yaml", &s)) { runtest_root_dir = 2; // build directory } else { fprintf(stderr, "Error: wsgidav found, but the config file wsgidav.yaml was not found\nTry running the tests from the dav root directory\n"); return 0; } } if(runtest_root_dir == 1) { if(!stat("build", &s)) { if(!S_ISDIR(s.st_mode)) { fprintf(stderr, "Error: build is not a directory: wsgidav disabled\n"); return 0; } } else { fprintf(stderr, "Error: build directory not found: wsgidav disabled\n"); return 0; } } return 1; } int wsgidav_start(void) { if(!wsgidav_exec_path) { return 1; // wsgidav_start called but wsgidav_is_available return 0 } if(runtest_root_dir == 1) { if(chdir("build")) { perror("chdir"); return 1; } } // delete previous testrepo directory char *args1[] = { "rm", "-Rf", "testrepo", NULL }; Process p_rm = process_spawn("/bin/rm", args1); cxmutstr err = process_err_read_string(&p_rm); if(process_close(&p_rm) != 0) { cxmutstr s = cx_strtrim(err); fprintf(stderr, "rm failed: %.*s\n", (int)s.length, s.ptr); return 1; } free(err.ptr); // copy testrepo to the build directory char *args2[] = { "cp", "-R", "../test/wsgidav/testrepo", "testrepo", NULL }; Process p_cp = process_spawn("/bin/cp", args2); err = process_err_read_string(&p_cp); if(process_close(&p_cp) != 0) { cxmutstr s = cx_strtrim(err); fprintf(stderr, "cp failed: %.*s\n", (int)s.length, s.ptr); return 1; } free(err.ptr); // start wsgidav char *args3[] = { "wsgidav", "-c", "../test/wsgidav/wsgidav.yaml", NULL }; wsgidav_process = process_spawn(wsgidav_exec_path, args3); // TODO: parse log or try to connect to localhost sleep(1); return 0; } int wsgidav_stop(void) { if(wsgidav_process.pid == 0) { return 0; } kill(wsgidav_process.pid, SIGKILL); (void)process_close(&wsgidav_process); //if(status > 2) { // printf("wsgidav exit: %d\n", status); //} return 0; } /* #else int wsgidav_is_available(void) { return 0; } int wsgidav_start(void) { return 1; } int wsgidav_stop(void) { return 1; } #endif */