2 * Copyright © 2018 Endless Mobile, Inc
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the licence, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 * Author: Philip Withnall <withnall@endlessm.com>
21 #include <glib/gstdio.h>
24 #include "service/dconf-generated.h"
25 #include "service/dconf-writer.h"
27 static guint n_warnings
= 0;
29 static GLogWriterOutput
30 log_writer_cb (GLogLevelFlags log_level
,
31 const GLogField
*fields
,
35 if (log_level
& G_LOG_LEVEL_WARNING
)
38 return G_LOG_WRITER_HANDLED
;
42 assert_n_warnings (guint expected_n_warnings
)
44 g_assert_cmpuint (n_warnings
, ==, expected_n_warnings
);
50 gchar
*dconf_dir
; /* (owned) */
53 gchar
*config_dir
= NULL
;
56 set_up (Fixture
*fixture
,
57 gconstpointer test_data
)
59 fixture
->dconf_dir
= g_build_filename (config_dir
, "dconf", NULL
);
60 g_assert_cmpint (g_mkdir (fixture
->dconf_dir
, 0755), ==, 0);
62 g_test_message ("Using dconf directory: %s", fixture
->dconf_dir
);
66 tear_down (Fixture
*fixture
,
67 gconstpointer test_data
)
69 g_assert_cmpint (g_rmdir (fixture
->dconf_dir
), ==, 0);
70 g_clear_pointer (&fixture
->dconf_dir
, g_free
);
72 assert_n_warnings (0);
75 /* Test basic initialisation of a #DConfWriter. This is essentially a smoketest. */
77 test_writer_basic (Fixture
*fixture
,
78 gconstpointer test_data
)
80 g_autoptr(DConfWriter
) writer
= NULL
;
82 writer
= DCONF_WRITER (dconf_writer_new (DCONF_TYPE_WRITER
, "some-name"));
83 g_assert_nonnull (writer
);
85 g_assert_cmpstr (dconf_writer_get_name (writer
), ==, "some-name");
88 /* Test that beginning a write operation when no database exists succeeds. Note
89 * that the database will not actually be created until some changes are made
90 * and the write is committed. */
92 test_writer_begin_missing (Fixture
*fixture
,
93 gconstpointer test_data
)
95 g_autoptr(DConfWriter
) writer
= NULL
;
96 DConfWriterClass
*writer_class
;
98 g_autoptr(GError
) local_error
= NULL
;
99 g_autofree gchar
*db_filename
= g_build_filename (fixture
->dconf_dir
, "missing", NULL
);
101 /* Check the database doesn’t exist. */
102 g_assert_false (g_file_test (db_filename
, G_FILE_TEST_EXISTS
));
104 /* Create a writer. */
105 writer
= DCONF_WRITER (dconf_writer_new (DCONF_TYPE_WRITER
, "missing"));
106 g_assert_nonnull (writer
);
108 writer_class
= DCONF_WRITER_GET_CLASS (writer
);
109 retval
= writer_class
->begin (writer
, &local_error
);
110 g_assert_no_error (local_error
);
111 g_assert_true (retval
);
114 /* Test that beginning a write operation when a corrupt or empty database exists
115 * will take a backup of the database and then succeed. Note that a new empty
116 * database will not actually be created until some changes are made and the
117 * write is committed. */
120 const gchar
*corrupt_db_contents
;
121 guint n_existing_backups
;
122 } BeginCorruptFileData
;
125 test_writer_begin_corrupt_file (Fixture
*fixture
,
126 gconstpointer test_data
)
128 const BeginCorruptFileData
*data
= test_data
;
129 g_autoptr(DConfWriter
) writer
= NULL
;
130 DConfWriterClass
*writer_class
;
132 g_autoptr(GError
) local_error
= NULL
;
133 g_autofree gchar
*db_filename
= g_build_filename (fixture
->dconf_dir
, "corrupt", NULL
);
134 g_autofree gchar
*new_db_filename_backup
= NULL
;
135 g_autofree gchar
*backup_file_contents
= NULL
;
136 gsize backup_file_contents_len
= 0;
139 /* Create a corrupt database. */
140 g_file_set_contents (db_filename
, data
->corrupt_db_contents
, -1, &local_error
);
141 g_assert_no_error (local_error
);
143 /* Create any existing backups, to test we don’t overwrite them. */
144 for (i
= 0; i
< data
->n_existing_backups
; i
++)
146 g_autofree gchar
*db_filename_backup
= g_strdup_printf ("%s~%u", db_filename
, i
);
147 g_file_set_contents (db_filename_backup
, "backup", -1, &local_error
);
148 g_assert_no_error (local_error
);
151 new_db_filename_backup
= g_strdup_printf ("%s~%u", db_filename
, data
->n_existing_backups
);
153 /* Create a writer. */
154 writer
= DCONF_WRITER (dconf_writer_new (DCONF_TYPE_WRITER
, "corrupt"));
155 g_assert_nonnull (writer
);
157 writer_class
= DCONF_WRITER_GET_CLASS (writer
);
158 retval
= writer_class
->begin (writer
, &local_error
);
159 g_assert_no_error (local_error
);
160 g_assert_true (retval
);
162 /* The writer should have printed a warning about the corrupt database. */
163 assert_n_warnings (1);
165 /* Check a backup file has been created and has the right content. */
166 g_file_get_contents (new_db_filename_backup
, &backup_file_contents
,
167 &backup_file_contents_len
, &local_error
);
168 g_assert_no_error (local_error
);
169 g_assert_cmpstr (backup_file_contents
, ==, data
->corrupt_db_contents
);
170 g_assert_cmpuint (backup_file_contents_len
, ==, strlen (data
->corrupt_db_contents
));
173 g_assert_cmpint (g_unlink (new_db_filename_backup
), ==, 0);
175 for (i
= 0; i
< data
->n_existing_backups
; i
++)
177 g_autofree gchar
*db_filename_backup
= g_strdup_printf ("%s~%u", db_filename
, i
);
178 g_assert_cmpint (g_unlink (db_filename_backup
), ==, 0);
183 main (int argc
, char **argv
)
185 g_autoptr(GError
) local_error
= NULL
;
187 const BeginCorruptFileData empty_data
= { "", 0 };
188 const BeginCorruptFileData corrupt_file_data0
= {
189 "secretly not a valid GVDB database 😧", 0
191 const BeginCorruptFileData corrupt_file_data1
= {
192 "secretly not a valid GVDB database 😧", 1
194 const BeginCorruptFileData corrupt_file_data2
= {
195 "secretly not a valid GVDB database 😧", 2
198 setlocale (LC_ALL
, "");
200 g_test_init (&argc
, &argv
, NULL
);
202 /* Set up a fake $XDG_CONFIG_HOME. We can’t do this in the fixture, as
203 * g_get_user_config_dir() caches its return value. */
204 config_dir
= g_dir_make_tmp ("dconf-test-writer_XXXXXX", &local_error
);
205 g_assert_no_error (local_error
);
206 g_assert_true (g_setenv ("XDG_CONFIG_HOME", config_dir
, TRUE
));
207 g_test_message ("Using config directory: %s", config_dir
);
209 /* Log handling so we don’t abort on the first g_warning(). */
210 g_log_set_writer_func (log_writer_cb
, NULL
, NULL
);
212 g_test_add ("/writer/basic", Fixture
, NULL
, set_up
,
213 test_writer_basic
, tear_down
);
214 g_test_add ("/writer/begin/missing", Fixture
, NULL
, set_up
,
215 test_writer_begin_missing
, tear_down
);
216 g_test_add ("/writer/begin/empty", Fixture
, &empty_data
, set_up
,
217 test_writer_begin_corrupt_file
, tear_down
);
218 g_test_add ("/writer/begin/corrupt-file/0", Fixture
, &corrupt_file_data0
, set_up
,
219 test_writer_begin_corrupt_file
, tear_down
);
220 g_test_add ("/writer/begin/corrupt-file/1", Fixture
, &corrupt_file_data1
, set_up
,
221 test_writer_begin_corrupt_file
, tear_down
);
222 g_test_add ("/writer/begin/corrupt-file/2", Fixture
, &corrupt_file_data2
, set_up
,
223 test_writer_begin_corrupt_file
, tear_down
);
225 retval
= g_test_run ();
227 /* Clean up the config dir. */
228 g_unsetenv ("XDG_CONFIG_HOME");
229 g_assert_cmpint (g_rmdir (config_dir
), ==, 0);
230 g_clear_pointer (&config_dir
, g_free
);