Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
test_conf_value_lifetime.c
Go to the documentation of this file.
1/*
2 This file is part of Ansel,
3 Copyright (C) 2026 Aurélien PIERRE.
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
44#include "common/conf.h"
45#include "darktable.h"
46
47#include <glib.h>
48#include <glib/gstdio.h>
49#include <stdarg.h>
50#include <stddef.h>
51#include <stdlib.h> // calloc/free, used directly below
52// cmocka.h declares `extern jmp_buf global_expect_assert_env' at file scope without including
53// <setjmp.h> itself. Same suppression as test_pipe_cache_policy.c, and for the same reason.
54#include <setjmp.h> // NOLINT(misc-include-cleaner)
55#include <stdint.h>
56#include <cmocka.h>
57
59#define TEST_KEY "plugins/test/conf_value_lifetime"
60
61static char *_rcfile = NULL;
62
63static int _setup(void **state)
64{
65 (void)state;
66 // dt_conf_init() writes through the darktable.conf global, so it must point at the
67 // instance being initialised before the call -- exactly as dt_init() does it.
68 _rcfile = g_build_filename(g_get_tmp_dir(), "ansel_test_conf_lifetime.rc", NULL);
69 g_remove(_rcfile);
70 darktable.conf = (dt_conf_t *)calloc(1, sizeof(dt_conf_t));
72 return 0;
73}
74
75static int _teardown(void **state)
76{
77 (void)state;
79 free(darktable.conf);
80 darktable.conf = NULL;
81 g_remove(_rcfile);
82 g_free(_rcfile);
83 _rcfile = NULL;
84 return 0;
85}
86
89{
90 (void)state;
92
93 const char *borrowed = dt_conf_get_string_const(TEST_KEY);
94 assert_string_equal(borrowed, "first");
95
96 // The write that used to free `borrowed` out from under us.
98
99 // Still readable, and still the value it was read as. Under ASAN, a regression here is a
100 // heap-use-after-free rather than a failed assertion.
101 assert_string_equal(borrowed, "first");
102
103 // ... while a fresh read observes the new value. Stale, not wrong.
104 assert_string_equal(dt_conf_get_string_const(TEST_KEY), "second");
105}
106
109{
110 (void)state;
111 const char *held[8];
112
113 for(int i = 0; i < 8; i++)
114 {
115 char *value = g_strdup_printf("value-%d", i);
117 g_free(value);
119 }
120
121 // Every pointer taken along the way still reads as what it was when taken.
122 for(int i = 0; i < 8; i++)
123 {
124 char *expected = g_strdup_printf("value-%d", i);
125 assert_string_equal(held[i], expected);
126 g_free(expected);
127 }
128}
129
136{
137 (void)state;
138 dt_conf_set_string(TEST_KEY, "unchanged");
139 const char *before = dt_conf_get_string_const(TEST_KEY);
140
141 for(int i = 0; i < 16; i++) dt_conf_set_string(TEST_KEY, "unchanged");
142
143 // Same allocation, so nothing was displaced and nothing was retired.
144 assert_ptr_equal(before, dt_conf_get_string_const(TEST_KEY));
145 assert_string_equal(before, "unchanged");
146}
147
150{
151 (void)state;
153 const char *borrowed = dt_conf_get_string_const(TEST_KEY);
154 assert_string_equal(borrowed, "11");
155
157
158 assert_string_equal(borrowed, "11");
159 assert_int_equal(dt_conf_get_int_fast(TEST_KEY), 22);
160}
161
162int main(void)
163{
164 const struct CMUnitTest tests[] = {
168 cmocka_unit_test(_typed_setters_retire_too),
169 };
170
171 return cmocka_run_group_tests(tests, _setup, _teardown);
172}
173
174// clang-format off
175// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
176// vim: shiftwidth=2 expandtab tabstop=2 cindent
177// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
178// clang-format on
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_conf_cleanup(dt_conf_t *cf)
Free everything cf owns. No dt_conf_*() call is valid afterwards.
void dt_conf_init(dt_conf_t *cf, const char *filename, GSList *override_entries)
Populate cf from filename and from the generated defaults.
int dt_conf_get_int_fast(const char *name)
Stored integer for name, NOT clamped to its declared bounds.
void dt_conf_set_int(const char *name, int val)
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
Borrow the stored string for name without copying it.
darktable_t darktable
Definition darktable.c:213
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
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.
Definition conf.h:99
static int _setup(void **state)
static int _teardown(void **state)
static void _borrowed_value_survives_being_replaced(void **state)
static void _every_displaced_value_stays_readable(void **state)
#define TEST_KEY
int main(void)
static char * _rcfile
static void _typed_setters_retire_too(void **state)
static void _rewriting_the_same_value_retires_nothing(void **state)