Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
test_conf_save.c
Go to the documentation of this file.
1/*
2 This file is part of Ansel,
3 Copyright (C) 2026 Guillaume Stutin.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18
35#include "common/conf.h"
36#include "darktable.h"
37#include "system/macros.h"
38
39#include <glib.h>
40#include <glib/gstdio.h>
41#include <stdarg.h>
42#include <stddef.h>
43#include <stdio.h>
44#include <stdlib.h> // calloc/free, used directly below
45#include <string.h>
46// cmocka.h declares `extern jmp_buf global_expect_assert_env' at file scope without including
47// <setjmp.h> itself. Same suppression as test_pipe_cache_policy.c, and for the same reason.
48#include <setjmp.h> // NOLINT(misc-include-cleaner)
49#include <stdint.h>
50#include <cmocka.h>
51
52#ifdef _WIN32
53#include "win/main_wrapper.h"
54#endif
55
57#define TEST_KEY "plugins/test/conf_save"
58
59static char *_dir = NULL;
60static char *_rcfile = NULL;
61
62static int _count_entries(const char *dir)
63{
64 GDir *d = g_dir_open(dir, 0, NULL);
65 assert_non_null(d);
66 int count = 0;
67 while(g_dir_read_name(d)) count++;
68 g_dir_close(d);
69 return count;
70}
71
72static int _setup(void **state __attribute__((unused)))
73{
74 _dir = g_dir_make_tmp("ansel_test_conf_save_XXXXXX", NULL);
75 if(IS_NULL_PTR(_dir)) return -1;
76 _rcfile = g_build_filename(_dir, "anselrc", NULL);
77 // dt_conf_init() writes through the darktable.conf global, so it must point at the
78 // instance being initialised before the call -- exactly as dt_init() does it.
79 darktable.conf = (dt_conf_t *)calloc(1, sizeof(dt_conf_t));
81 return 0;
82}
83
84static int _teardown(void **state __attribute__((unused)))
85{
87 free(darktable.conf);
88 darktable.conf = NULL;
89
90 // Everything the tests left in the directory: the settings file and the blocker of
91 // _a_failed_flush_returns_1_and_writes_nothing().
92 GDir *d = g_dir_open(_dir, 0, NULL);
93 if(!IS_NULL_PTR(d))
94 {
95 const char *name = NULL;
96 while((name = g_dir_read_name(d)))
97 {
98 char *path = g_build_filename(_dir, name, NULL);
99 g_remove(path);
100 g_free(path);
101 }
102 g_dir_close(d);
103 }
104 g_rmdir(_dir);
105 g_free(_rcfile);
106 g_free(_dir);
107 _rcfile = NULL;
108 _dir = NULL;
109 return 0;
110}
111
114static void _flush_writes_what_was_set(void **state __attribute__((unused)))
115{
117 assert_int_equal(dt_conf_flush(), 0);
118
119 char *contents = NULL;
120 assert_true(g_file_get_contents(_rcfile, &contents, NULL, NULL));
121 const gboolean written = !IS_NULL_PTR(strstr(contents, TEST_KEY "=saved\n"));
122 g_free(contents);
123 assert_true(written);
124
125 assert_int_equal(_count_entries(_dir), 1);
126}
127
131{
132#ifdef _WIN32
133 // Windows refuses to rename over a file that is open without FILE_SHARE_DELETE, which a
134 // stdio handle is not: the observable this test relies on does not exist there.
135 skip();
136#endif
137 dt_conf_set_string(TEST_KEY, "before");
138 assert_int_equal(dt_conf_flush(), 0);
139
140 FILE *old = g_fopen(_rcfile, "rb");
141 assert_non_null(old);
142
144 assert_int_equal(dt_conf_flush(), 0);
145
146 // Read the whole old file through the handle opened before the second flush.
147 GString *seen = g_string_new(NULL);
148 char buffer[4096];
149 size_t n = 0;
150 while((n = fread(buffer, 1, sizeof(buffer), old)) > 0) g_string_append_len(seen, buffer, n);
151 fclose(old);
152 const gboolean old_intact = !IS_NULL_PTR(strstr(seen->str, TEST_KEY "=before\n"));
153 g_string_free(seen, TRUE);
154 assert_true(old_intact);
155
156 char *contents = NULL;
157 assert_true(g_file_get_contents(_rcfile, &contents, NULL, NULL));
158 const gboolean new_written = !IS_NULL_PTR(strstr(contents, TEST_KEY "=after\n"));
159 g_free(contents);
160 assert_true(new_written);
161}
162
166{
167 char *blocker = g_build_filename(_dir, "not-a-directory", NULL);
168 assert_true(g_file_set_contents(blocker, "x", 1, NULL));
169 char *unwritable = g_build_filename(blocker, "anselrc", NULL);
170 const int entries = _count_entries(_dir);
171
172 char saved[sizeof(darktable.conf->filename)];
173 g_strlcpy(saved, darktable.conf->filename, sizeof(saved));
174 g_strlcpy(darktable.conf->filename, unwritable, sizeof(darktable.conf->filename));
175
176 dt_conf_set_string(TEST_KEY, "unwritable");
177 const int result = dt_conf_flush();
178
179 g_strlcpy(darktable.conf->filename, saved, sizeof(darktable.conf->filename));
180 g_free(unwritable);
181
182 char *contents = NULL;
183 assert_true(g_file_get_contents(blocker, &contents, NULL, NULL));
184 const gboolean blocker_intact = !g_strcmp0(contents, "x");
185 g_free(contents);
186 g_free(blocker);
187
188 assert_int_equal(result, 1);
189 assert_true(blocker_intact);
190 assert_int_equal(_count_entries(_dir), entries);
191}
192
193int main(int argc, char *argv[])
194{
195 const struct CMUnitTest tests[] = {
196 cmocka_unit_test(_flush_writes_what_was_set),
199 };
200
201 return cmocka_run_group_tests(tests, _setup, _teardown);
202}
203
204// clang-format off
205// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
206// vim: shiftwidth=2 expandtab tabstop=2 cindent
207// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
208// clang-format on
#define TRUE
Definition ashift_lsd.c:162
void dt_conf_cleanup(dt_conf_t *cf)
Free everything cf owns. No dt_conf_*() call is valid afterwards.
int dt_conf_flush(void)
Write the settings to their file now. Overridden keys keep their stored value.
void dt_conf_init(dt_conf_t *cf, const char *filename, GSList *override_entries)
Populate cf from filename and from the generated defaults.
void dt_conf_set_string(const char *name, const char *val)
darktable_t darktable
Definition darktable.c:213
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
const char * name
Definition pdf.h:90
int main()
Definition prova.c:47
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
Definition simd.h:55
const float uint32_t state[4]
struct dt_conf_t * conf
Definition darktable.h:184
The whole configuration state. One instance lives at darktable.conf.
char filename[DT_PATH_MAX]
GHashTable * entries
Definition supervisor.c:120
static char * _dir
static void _flush_writes_what_was_set(void **state __attribute__((unused)))
static void _a_failed_flush_returns_1_and_writes_nothing(void **state __attribute__((unused)))
static int _teardown(void **state __attribute__((unused)))
#define TEST_KEY
static void _flush_replaces_the_file_rather_than_rewriting_it(void **state __attribute__((unused)))
static int _setup(void **state __attribute__((unused)))
static char * _rcfile
static int _count_entries(const char *dir)