[dconf: 4/9] tests: Add unit tests for some of service/dconf-writer.c



commit ed13467de71084e44fc98a9a0e3a2fe99ae6f26c
Author: Philip Withnall <withnall endlessm com>
Date:   Mon Aug 13 15:01:53 2018 +0100

    tests: Add unit tests for some of service/dconf-writer.c
    
    Signed-off-by: Philip Withnall <withnall endlessm com>

 tests/meson.build |   2 +
 tests/writer.c    | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 235 insertions(+)
---
diff --git a/tests/meson.build b/tests/meson.build
index ef0b940..3274059 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -30,6 +30,7 @@ unit_tests = [
   ['gdbus-filter', 'dbus.c', '-DDBUS_BACKEND="/gdbus/filter"', libdconf_gdbus_filter_dep, []],
   ['engine', 'engine.c', '-DSRCDIR="@0@"'.format(test_dir), [dl_dep, libdconf_engine_dep, m_dep], 
libdconf_mock],
   ['client', 'client.c', '-DSRCDIR="@0@"'.format(test_dir), [libdconf_client_dep, libdconf_engine_dep], 
libdconf_mock],
+  ['writer', 'writer.c', '-DSRCDIR="@0@"'.format(test_dir), [glib_dep, dl_dep, m_dep], [libdconf_service, 
libdconf_mock]],
 ]
 
 foreach unit_test: unit_tests
@@ -39,6 +40,7 @@ foreach unit_test: unit_tests
     c_args: unit_test[2],
     dependencies: unit_test[3],
     link_with: unit_test[4],
+    include_directories: [top_inc, include_directories('../service')],
   )
 
   test(unit_test[0], exe, is_parallel: false, env: envs)
diff --git a/tests/writer.c b/tests/writer.c
new file mode 100644
index 0000000..955ba91
--- /dev/null
+++ b/tests/writer.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright © 2018 Endless Mobile, 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 of the licence, 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/>.
+ *
+ * Author: Philip Withnall <withnall endlessm com>
+ */
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <locale.h>
+
+#include "service/dconf-generated.h"
+#include "service/dconf-writer.h"
+
+static guint n_warnings = 0;
+
+static GLogWriterOutput
+log_writer_cb (GLogLevelFlags   log_level,
+               const GLogField *fields,
+               gsize            n_fields,
+               gpointer         user_data)
+{
+  if (log_level & G_LOG_LEVEL_WARNING)
+    n_warnings++;
+
+  return G_LOG_WRITER_HANDLED;
+}
+
+static void
+assert_n_warnings (guint expected_n_warnings)
+{
+  g_assert_cmpuint (n_warnings, ==, expected_n_warnings);
+  n_warnings = 0;
+}
+
+typedef struct
+{
+  gchar *dconf_dir;  /* (owned) */
+} Fixture;
+
+gchar *config_dir = NULL;
+
+static void
+set_up (Fixture       *fixture,
+        gconstpointer  test_data)
+{
+  fixture->dconf_dir = g_build_filename (config_dir, "dconf", NULL);
+  g_assert_cmpint (g_mkdir (fixture->dconf_dir, 0755), ==, 0);
+
+  g_test_message ("Using dconf directory: %s", fixture->dconf_dir);
+}
+
+static void
+tear_down (Fixture       *fixture,
+           gconstpointer  test_data)
+{
+  g_assert_cmpint (g_rmdir (fixture->dconf_dir), ==, 0);
+  g_clear_pointer (&fixture->dconf_dir, g_free);
+
+  assert_n_warnings (0);
+}
+
+/* Test basic initialisation of a #DConfWriter. This is essentially a smoketest. */
+static void
+test_writer_basic (Fixture       *fixture,
+                   gconstpointer  test_data)
+{
+  g_autoptr(DConfWriter) writer = NULL;
+
+  writer = DCONF_WRITER (dconf_writer_new (DCONF_TYPE_WRITER, "some-name"));
+  g_assert_nonnull (writer);
+
+  g_assert_cmpstr (dconf_writer_get_name (writer), ==, "some-name");
+}
+
+/* Test that beginning a write operation when no database exists succeeds. Note
+ * that the database will not actually be created until some changes are made
+ * and the write is committed. */
+static void
+test_writer_begin_missing (Fixture       *fixture,
+                           gconstpointer  test_data)
+{
+  g_autoptr(DConfWriter) writer = NULL;
+  DConfWriterClass *writer_class;
+  gboolean retval;
+  g_autoptr(GError) local_error = NULL;
+  g_autofree gchar *db_filename = g_build_filename (fixture->dconf_dir, "missing", NULL);
+
+  /* Check the database doesn’t exist. */
+  g_assert_false (g_file_test (db_filename, G_FILE_TEST_EXISTS));
+
+  /* Create a writer. */
+  writer = DCONF_WRITER (dconf_writer_new (DCONF_TYPE_WRITER, "missing"));
+  g_assert_nonnull (writer);
+
+  writer_class = DCONF_WRITER_GET_CLASS (writer);
+  retval = writer_class->begin (writer, &local_error);
+  g_assert_no_error (local_error);
+  g_assert_true (retval);
+}
+
+/* Test that beginning a write operation when a corrupt or empty database exists
+ * will take a backup of the database and then succeed. Note that a new empty
+ * database will not actually be created until some changes are made and the
+ * write is committed. */
+typedef struct
+{
+  const gchar *corrupt_db_contents;
+  guint n_existing_backups;
+} BeginCorruptFileData;
+
+static void
+test_writer_begin_corrupt_file (Fixture       *fixture,
+                                gconstpointer  test_data)
+{
+  const BeginCorruptFileData *data = test_data;
+  g_autoptr(DConfWriter) writer = NULL;
+  DConfWriterClass *writer_class;
+  gboolean retval;
+  g_autoptr(GError) local_error = NULL;
+  g_autofree gchar *db_filename = g_build_filename (fixture->dconf_dir, "corrupt", NULL);
+  g_autofree gchar *new_db_filename_backup = NULL;
+  g_autofree gchar *backup_file_contents = NULL;
+  gsize backup_file_contents_len = 0;
+  guint i;
+
+  /* Create a corrupt database. */
+  g_file_set_contents (db_filename, data->corrupt_db_contents, -1, &local_error);
+  g_assert_no_error (local_error);
+
+  /* Create any existing backups, to test we don’t overwrite them. */
+  for (i = 0; i < data->n_existing_backups; i++)
+    {
+      g_autofree gchar *db_filename_backup = g_strdup_printf ("%s~%u", db_filename, i);
+      g_file_set_contents (db_filename_backup, "backup", -1, &local_error);
+      g_assert_no_error (local_error);
+    }
+
+  new_db_filename_backup = g_strdup_printf ("%s~%u", db_filename, data->n_existing_backups);
+
+  /* Create a writer. */
+  writer = DCONF_WRITER (dconf_writer_new (DCONF_TYPE_WRITER, "corrupt"));
+  g_assert_nonnull (writer);
+
+  writer_class = DCONF_WRITER_GET_CLASS (writer);
+  retval = writer_class->begin (writer, &local_error);
+  g_assert_no_error (local_error);
+  g_assert_true (retval);
+
+  /* The writer should have printed a warning about the corrupt database. */
+  assert_n_warnings (1);
+
+  /* Check a backup file has been created and has the right content. */
+  g_file_get_contents (new_db_filename_backup, &backup_file_contents,
+                       &backup_file_contents_len, &local_error);
+  g_assert_no_error (local_error);
+  g_assert_cmpstr (backup_file_contents, ==, data->corrupt_db_contents);
+  g_assert_cmpuint (backup_file_contents_len, ==, strlen (data->corrupt_db_contents));
+
+  /* Clean up. */
+  g_assert_cmpint (g_unlink (new_db_filename_backup), ==, 0);
+
+  for (i = 0; i < data->n_existing_backups; i++)
+    {
+      g_autofree gchar *db_filename_backup = g_strdup_printf ("%s~%u", db_filename, i);
+      g_assert_cmpint (g_unlink (db_filename_backup), ==, 0);
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  g_autoptr(GError) local_error = NULL;
+  int retval;
+  const BeginCorruptFileData empty_data = { "", 0 };
+  const BeginCorruptFileData corrupt_file_data0 = {
+    "secretly not a valid GVDB database 😧", 0
+  };
+  const BeginCorruptFileData corrupt_file_data1 = {
+    "secretly not a valid GVDB database 😧", 1
+  };
+  const BeginCorruptFileData corrupt_file_data2 = {
+    "secretly not a valid GVDB database 😧", 2
+  };
+
+  setlocale (LC_ALL, "");
+
+  g_test_init (&argc, &argv, NULL);
+
+  /* Set up a fake $XDG_CONFIG_HOME. We can’t do this in the fixture, as
+   * g_get_user_config_dir() caches its return value. */
+  config_dir = g_dir_make_tmp ("dconf-test-writer_XXXXXX", &local_error);
+  g_assert_no_error (local_error);
+  g_assert_true (g_setenv ("XDG_CONFIG_HOME", config_dir, TRUE));
+  g_test_message ("Using config directory: %s", config_dir);
+
+  /* Log handling so we don’t abort on the first g_warning(). */
+  g_log_set_writer_func (log_writer_cb, NULL, NULL);
+
+  g_test_add ("/writer/basic", Fixture, NULL, set_up,
+              test_writer_basic, tear_down);
+  g_test_add ("/writer/begin/missing", Fixture, NULL, set_up,
+              test_writer_begin_missing, tear_down);
+  g_test_add ("/writer/begin/empty", Fixture, &empty_data, set_up,
+              test_writer_begin_corrupt_file, tear_down);
+  g_test_add ("/writer/begin/corrupt-file/0", Fixture, &corrupt_file_data0, set_up,
+              test_writer_begin_corrupt_file, tear_down);
+  g_test_add ("/writer/begin/corrupt-file/1", Fixture, &corrupt_file_data1, set_up,
+              test_writer_begin_corrupt_file, tear_down);
+  g_test_add ("/writer/begin/corrupt-file/2", Fixture, &corrupt_file_data2, set_up,
+              test_writer_begin_corrupt_file, tear_down);
+
+  retval = g_test_run ();
+
+  /* Clean up the config dir. */
+  g_unsetenv ("XDG_CONFIG_HOME");
+  g_assert_cmpint (g_rmdir (config_dir), ==, 0);
+  g_clear_pointer (&config_dir, g_free);
+
+  return retval;
+}


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