[phodav: 5/6] server+ci: test PhodavVirtualDir




commit 5951d1659fb1d55ba2959c0bd1b7b91e94d3ecf5
Author: Jakub Janků <jjanku redhat com>
Date:   Sun Jul 12 19:18:21 2020 +0200

    server+ci: test PhodavVirtualDir
    
    Add new project subdir "tests".
    
    Add "tests/virtual-dir-server.c". This is in principle similar
    to chezdav but uses the new PhodavVirtualDir functions.
    
    Test virtual-dir-server using litmus in Gitlab CI
    to show that the behavior of phodav is not affected when using
    the "virtual root".
    
    Also test virtual-dir-server using "tests/virtual-dir.c" which
    aims to cover the parts litmus cannot. This is quite limited
    and only checks that the correct status is returned.
    
    Signed-off-by: Jakub Janků <jjanku redhat com>

 .gitlab-ci.yml             |   6 ++
 meson.build                |   1 +
 tests/meson.build          |  21 +++++++
 tests/virtual-dir-server.c | 107 ++++++++++++++++++++++++++++++++
 tests/virtual-dir.c        | 150 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 285 insertions(+)
---
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c906e32..2098e4a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -48,6 +48,7 @@ build:
     paths:
       - build/meson-logs/*.txt
       - dev
+      - build-no-avahi/tests/virtual-dir-server
     expire_in: 1 week
     when: always
 
@@ -85,4 +86,9 @@ litmus:
     - ./dev/bin/chezdav &
     # wait for the port to open
     - while ! nc -z localhost 8080; do sleep 1s; done
+    - litmus --keep-going dav://localhost:8080/
+    - kill $!
+    # check that virtual root works as well
+    - ./build-no-avahi/tests/virtual-dir-server &
+    - while ! nc -z localhost 8080; do sleep 1s; done
     - litmus dav://localhost:8080/
diff --git a/meson.build b/meson.build
index de026e7..b8ff125 100644
--- a/meson.build
+++ b/meson.build
@@ -54,6 +54,7 @@ subdir('libphodav')
 subdir('bin')
 subdir('doc')
 subdir('data')
+subdir('tests')
 
 config = {
   'GETTEXT_PACKAGE' : proj_gettext,
diff --git a/tests/meson.build b/tests/meson.build
new file mode 100644
index 0000000..aeb48e3
--- /dev/null
+++ b/tests/meson.build
@@ -0,0 +1,21 @@
+tests_sources = []
+
+if host_machine.system() != 'windows'
+  tests_sources += 'virtual-dir.c'
+endif
+
+executable('virtual-dir-server',
+           sources : 'virtual-dir-server.c',
+           include_directories: incdir,
+           link_with : libphodav,
+           dependencies : deps)
+
+foreach src : tests_sources
+  name = 'test-@0@'.format(src).split('.')[0]
+  exe = executable(name,
+                   sources : src,
+                   include_directories: incdir,
+                   link_with : libphodav,
+                   dependencies : deps)
+  test(name, exe)
+endforeach
diff --git a/tests/virtual-dir-server.c b/tests/virtual-dir-server.c
new file mode 100644
index 0000000..0da2895
--- /dev/null
+++ b/tests/virtual-dir-server.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stdlib.h>
+#include <glib.h>
+
+#include "libphodav/phodav.h"
+
+#define PORT 8080
+
+static void
+create_test_file (const gchar *path)
+{
+  GFile *f = g_file_new_for_path (path);
+  GFileOutputStream *out = g_file_replace (f, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL);
+  g_output_stream_printf (G_OUTPUT_STREAM (out), NULL, NULL, NULL, "test data");
+  g_object_unref (out);
+  g_object_unref (f);
+}
+
+/* Taken from phodav-method-delete.c and simplified */
+static void
+delete_file_recursive (GFile *file)
+{
+  GFileEnumerator *e;
+  e = g_file_enumerate_children (file, "standard::*", G_FILE_QUERY_INFO_NONE, NULL, NULL);
+  if (e)
+    {
+      while (TRUE)
+        {
+          GFileInfo *info = g_file_enumerator_next_file (e, NULL, NULL);
+          if (!info)
+            break;
+          GFile *del = g_file_get_child (file, g_file_info_get_name (info));
+          delete_file_recursive (del);
+          g_object_unref (del);
+          g_object_unref (info);
+        }
+
+      g_file_enumerator_close (e, NULL, NULL);
+      g_clear_object (&e);
+    }
+
+  g_file_delete (file, NULL, NULL);
+}
+
+/* This is basically a very stripped-down version of chezdav
+ * for the purpose of testing PhodavVirtualDir.
+ *
+ * It creates a folder that acts as the "real" root and one virtual dir "/virtual"
+ * inside it - so the setup is similar to the one used by spice-gtk */
+int
+main (int argc, char *argv[])
+{
+  GFile *root_dir = g_file_new_for_path ("./phodav-virtual-root");
+  GFile *real_dir = g_file_get_child (root_dir, "real");
+
+  /* clean-up after any previous tests */
+  delete_file_recursive (root_dir);
+
+  /* setup local dir structure */
+  g_assert_true (g_file_make_directory (root_dir, NULL, NULL));
+  g_assert_true (g_file_make_directory (real_dir, NULL, NULL));
+  create_test_file ("./phodav-virtual-root/test.txt");
+
+  /* setup virtual dir structure */
+  PhodavVirtualDir *root = phodav_virtual_dir_new_root ();
+  phodav_virtual_dir_root_set_real (root, "./phodav-virtual-root");
+  PhodavVirtualDir *virtual_dir = phodav_virtual_dir_new_dir (root, "/virtual", NULL);
+  phodav_virtual_dir_attach_real_child (virtual_dir, real_dir);
+
+  PhodavServer *phodav = phodav_server_new_for_root_file (G_FILE(root));
+
+  g_object_unref (virtual_dir);
+  g_object_unref (real_dir);
+  g_object_unref (root_dir);
+  g_object_unref (root);
+
+  SoupServer *server = phodav_server_get_soup_server (phodav);
+  GError *error = NULL;
+  if (!soup_server_listen_all (server, PORT, 0, &error))
+    {
+      g_printerr ("Failed to listen on port %d: %s\n", PORT, error->message);
+      g_error_free (error);
+      return 1;
+    }
+
+  GMainLoop *loop = g_main_loop_new (NULL, FALSE);
+  g_main_loop_run (loop);
+  g_main_loop_unref (loop);
+  g_object_unref (phodav);
+
+  return 0;
+}
diff --git a/tests/virtual-dir.c b/tests/virtual-dir.c
new file mode 100644
index 0000000..4d5d9e9
--- /dev/null
+++ b/tests/virtual-dir.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2020 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/prctl.h>
+#include <glib.h>
+
+#include "libphodav/phodav.h"
+
+#define SERVER_URI "dav://localhost:8080/"
+
+static SoupSession *session;
+
+static const gchar test_put_data[] = "test_put: test data";
+
+typedef struct _TestCase {
+  const gchar *method;
+  const gchar *path;
+  guint        status_code;
+  const gchar *destination;
+} TestCase;
+
+static void
+test_generic (gconstpointer data)
+{
+  const TestCase *test = data;
+  gchar *uri = g_build_path ("/", SERVER_URI, test->path, NULL);
+  SoupMessage *msg = soup_message_new (test->method, uri);
+  g_free (uri);
+
+  if (test->method == SOUP_METHOD_COPY)
+    {
+      gchar *dest_uri = g_build_path ("/", SERVER_URI, test->destination, NULL);
+      soup_message_headers_append (msg->request_headers, "Destination", dest_uri);
+      g_free (dest_uri);
+    }
+
+  if (test->method == SOUP_METHOD_PUT)
+    {
+      soup_message_body_append (msg->request_body, SOUP_MEMORY_STATIC,
+                                test_put_data, strlen (test_put_data));
+    }
+
+  GInputStream *in = soup_session_send (session, msg, NULL, NULL);
+
+  g_assert_cmpint (msg->status_code, ==, test->status_code);
+
+  g_object_unref (in);
+  g_object_unref (msg);
+}
+
+static gchar *
+replace_char_dup (const gchar *str, gchar old, gchar new)
+{
+  gchar *copy = g_strdup (str);
+  gchar *pos = strchr (copy, old);
+  while (pos)
+    {
+      *pos = new;
+      pos = strchr (pos, old);
+    }
+  return copy;
+}
+
+static gchar *
+get_test_path (const TestCase *test)
+{
+  gchar *path = replace_char_dup (test->path, '/', '\\');
+  gchar *test_path = g_build_path ("/", "/", test->method, path, NULL);
+  g_free (path);
+  if (test->method == SOUP_METHOD_COPY)
+    {
+      gchar *dest = replace_char_dup (test->destination, '/', '\\');
+      gchar *tmp = g_strconcat (test_path, "->", dest, NULL);
+      g_free (dest);
+      g_free (test_path);
+      test_path = tmp;
+    }
+  return test_path;
+}
+
+int
+main (int argc, char *argv[])
+{
+  pid_t child_pid = fork ();
+  g_assert_cmpint (child_pid, !=, -1);
+  if (child_pid == 0)
+    {
+      prctl(PR_SET_PDEATHSIG, SIGTERM);
+      execl ("tests/virtual-dir-server", "virtual-dir-server", NULL);
+      g_printerr ("Error launching virtual-dir-server\n");
+      return 1;
+    }
+
+  g_test_init (&argc, &argv, NULL);
+
+  session = soup_session_new ();
+
+  /* the order matters, some tests depend on the previous ones */
+  const TestCase tests[] = {
+    {SOUP_METHOD_GET, "/", SOUP_STATUS_OK},
+    {SOUP_METHOD_GET, "/virtual", SOUP_STATUS_OK},
+    {SOUP_METHOD_GET, "/non-existent", SOUP_STATUS_NOT_FOUND},
+    {SOUP_METHOD_GET, "/virtual/non-existent", SOUP_STATUS_NOT_FOUND},
+    {SOUP_METHOD_GET, "/virtual/real", SOUP_STATUS_OK},
+
+    {SOUP_METHOD_MKCOL, "/A", SOUP_STATUS_CREATED},
+    {SOUP_METHOD_MKCOL, "/virtual/B", SOUP_STATUS_FORBIDDEN},
+    {SOUP_METHOD_MKCOL, "/virtual/real/B", SOUP_STATUS_CREATED},
+
+    {SOUP_METHOD_COPY, "/test.txt", SOUP_STATUS_CREATED, "/test-copy.txt"},
+    {SOUP_METHOD_COPY, "/virtual", SOUP_STATUS_FORBIDDEN, "/virtual-copy"},
+    {SOUP_METHOD_COPY, "/test.txt", SOUP_STATUS_FORBIDDEN, "/virtual/test-copy.txt"},
+    {SOUP_METHOD_COPY, "/test.txt", SOUP_STATUS_CREATED, "/virtual/real/test-copy.txt"},
+
+    {SOUP_METHOD_PUT, "/test-put.txt", SOUP_STATUS_CREATED},
+    {SOUP_METHOD_PUT, "/virtual/test-put.txt", SOUP_STATUS_INTERNAL_SERVER_ERROR},
+    {SOUP_METHOD_PUT, "/virtual/real/test-put.txt", SOUP_STATUS_CREATED},
+
+    {SOUP_METHOD_DELETE, "/A", SOUP_STATUS_NO_CONTENT},
+    {SOUP_METHOD_DELETE, "/virtual/real/B", SOUP_STATUS_NO_CONTENT},
+    {SOUP_METHOD_DELETE, "/virtual", SOUP_STATUS_FORBIDDEN},
+  };
+
+  for (guint i = 0; i < G_N_ELEMENTS (tests); i++)
+    {
+      gchar *test_path = get_test_path (&tests[i]);
+      g_test_add_data_func (test_path, &tests[i], test_generic);
+      g_free (test_path);
+    }
+
+  gint res = g_test_run ();
+  g_object_unref (session);
+  return res;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]