From ed13467de71084e44fc98a9a0e3a2fe99ae6f26c Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Mon, 13 Aug 2018 15:01:53 +0100 Subject: tests: Add unit tests for some of service/dconf-writer.c Signed-off-by: Philip Withnall --- tests/meson.build | 2 + tests/writer.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 tests/writer.c 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 . + * + * Author: Philip Withnall + */ + +#include +#include +#include + +#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; +} -- cgit v1.2.1