test/wsgidav.c

branch
dav-2
changeset 902
06fa328989ee
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/wsgidav.c	Sat Jan 24 13:56:19 2026 +0100
@@ -0,0 +1,306 @@
+/*
+ * 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
+
+*/
\ No newline at end of file

mercurial