Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
tests/styles.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2026 Guillaume Stutin.
4
5 darktable 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 darktable 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 darktable. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include "darktable.h"
20#include "database/database.h"
21#include "common/film.h"
23#include "common/image.h"
24#include "common/styles.h"
25
26#include <assert.h>
27#include <gio/gio.h>
28#include <glib/gstdio.h>
29#include <sqlite3.h>
30#include <stdio.h>
31#include <stdlib.h>
32
33#ifdef _WIN32
34#include "win/main_wrapper.h"
35#endif
36
37#ifndef ANSEL_TEST_SOURCE_DIR
38#define ANSEL_TEST_SOURCE_DIR "."
39#endif
40
41#ifndef ANSEL_TEST_BINARY_DIR
42#define ANSEL_TEST_BINARY_DIR "."
43#endif
44
59static char *test_image_dir = NULL;
60
74
75static int test_fail(const char *scenario, const char *message, char **failure_reason)
76{
77 fprintf(stderr, "[FAIL] %s: %s\n", scenario, message);
78 if(!IS_NULL_PTR(failure_reason) && IS_NULL_PTR(*failure_reason))
79 *failure_reason = g_strdup(message);
80 return 1;
81}
82
83static gboolean is_style_file(const char *filename)
84{
85 const char *extension = strrchr(filename, '.');
86 return !IS_NULL_PTR(extension) && !g_ascii_strcasecmp(extension, ".dtstyle");
87}
88
89static char *scenario_name_from_style_file(const char *filename)
90{
91 char *scenario = g_strdup(filename);
92 char *extension = strrchr(scenario, '.');
93 if(!IS_NULL_PTR(extension)) *extension = '\0';
94 return scenario;
95}
96
97static int sql_int_for_bound_images(const char *query, const int32_t imgid_a, const int32_t imgid_b)
98{
99 sqlite3_stmt *stmt = NULL;
100 sqlite3_prepare_v2(dt_database_get_sqlite3_global(), query, -1, &stmt, NULL);
101 sqlite3_bind_int(stmt, 1, imgid_a);
102 sqlite3_bind_int(stmt, 2, imgid_b);
103 const int value = (sqlite3_step(stmt) == SQLITE_ROW) ? sqlite3_column_int(stmt, 0) : -1;
104 sqlite3_finalize(stmt);
105 return value;
106}
107
108static int max_style_id(void)
109{
110 sqlite3_stmt *stmt = NULL;
111 sqlite3_prepare_v2(dt_database_get_sqlite3_global(), "SELECT COALESCE(MAX(id), 0) FROM data.styles", -1,
112 &stmt, NULL);
113 const int value = (sqlite3_step(stmt) == SQLITE_ROW) ? sqlite3_column_int(stmt, 0) : 0;
114 sqlite3_finalize(stmt);
115 return value;
116}
117
118static char *imported_style_name(const int before_import_style_id)
119{
120 sqlite3_stmt *stmt = NULL;
121 sqlite3_prepare_v2(dt_database_get_sqlite3_global(),
122 "SELECT name FROM data.styles WHERE id > ?1 ORDER BY id DESC LIMIT 1", -1, &stmt,
123 NULL);
124 sqlite3_bind_int(stmt, 1, before_import_style_id);
125
126 char *name = NULL;
127 if(sqlite3_step(stmt) == SQLITE_ROW)
128 name = g_strdup((const char *)sqlite3_column_text(stmt, 0));
129
130 sqlite3_finalize(stmt);
131 return name;
132}
133
134static int32_t create_test_image(const char *source_image_path)
135{
136 static int image_index = 0;
137 const char *extension = strrchr(source_image_path, '.');
138 char *filename = g_strdup_printf("style-test-%d%s", image_index++, extension ? extension : ".raw");
139 char *image_path = g_build_filename(test_image_dir, filename, NULL);
140
141 GFile *source_file = g_file_new_for_path(source_image_path);
142 GFile *image_file = g_file_new_for_path(image_path);
143 GError *error = NULL;
144 const gboolean copied = g_file_copy(source_file, image_file, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error);
145 g_object_unref(source_file);
146 g_object_unref(image_file);
147
148 if(!copied)
149 {
150 fprintf(stderr, "[FAIL] copy test image: %s\n", error ? error->message : "unknown error");
151 g_clear_error(&error);
152 dt_free(filename);
153 dt_free(image_path);
154 return 0;
155 }
156
157 dt_film_t film;
158 dt_film_init(&film);
159 if(dt_film_new(&film, test_image_dir) <= 0)
160 {
161 dt_film_cleanup(&film);
162 dt_free(filename);
163 dt_free(image_path);
164 return 0;
165 }
166
167 const int32_t imgid = dt_image_import(film.id, image_path, FALSE);
168
169 dt_film_cleanup(&film);
170 dt_free(filename);
171 dt_free(image_path);
172 return imgid;
173}
174
175static int load_xmp_on_image(const char *scenario, const int32_t imgid, const char *xmp_path,
176 char **failure_reason)
177{
178 if(!g_file_test(xmp_path, G_FILE_TEST_IS_REGULAR))
179 return test_fail(scenario, "missing XMP fixture", failure_reason);
180
181 return dt_history_load_and_apply(imgid, (gchar *)xmp_path, TRUE)
182 ? test_fail(scenario, "could not load XMP fixture", failure_reason)
183 : 0;
184}
185
186static int load_start_history(const char *scenario, const int32_t imgid, const char *start_xmp_path,
187 const char *start_xmp_name, char **failure_reason)
188{
189 if(g_file_test(start_xmp_path, G_FILE_TEST_IS_REGULAR))
190 return load_xmp_on_image(scenario, imgid, start_xmp_path, failure_reason);
191
192 printf("[NOTE] %s: no %s fixture, using imported base history\n", scenario, start_xmp_name);
193 return 0;
194}
195
196static void print_module_order_summary(const char *label, const int32_t imgid)
197{
198 sqlite3_stmt *stmt = NULL;
199 sqlite3_prepare_v2(dt_database_get_sqlite3_global(),
200 "SELECT version, iop_list FROM main.module_order WHERE imgid=?1", -1, &stmt, NULL);
201 sqlite3_bind_int(stmt, 1, imgid);
202
203 if(sqlite3_step(stmt) == SQLITE_ROW)
204 {
205 const unsigned char *iop_list = sqlite3_column_text(stmt, 1);
206 fprintf(stderr, " %s pipe order for image %d:\n", label, imgid);
207 fprintf(stderr, " version=%d\n", sqlite3_column_int(stmt, 0));
208 fprintf(stderr, " iop_list=%s\n", iop_list ? (const char *)iop_list : "(null)");
209 }
210 else
211 {
212 fprintf(stderr, " %s pipe order for image %d: no module_order row\n", label, imgid);
213 }
214
215 sqlite3_finalize(stmt);
216}
217
218static int compare_pipe_order(const char *scenario, const int32_t actual_imgid, const int32_t expected_imgid,
219 char **failure_reason)
220{
221 const int diff = sql_int_for_bound_images(
222 "SELECT"
223 " (SELECT COUNT(*) FROM"
224 " (SELECT version, iop_list FROM main.module_order WHERE imgid=?1"
225 " EXCEPT"
226 " SELECT version, iop_list FROM main.module_order WHERE imgid=?2))"
227 " +"
228 " (SELECT COUNT(*) FROM"
229 " (SELECT version, iop_list FROM main.module_order WHERE imgid=?2"
230 " EXCEPT"
231 " SELECT version, iop_list FROM main.module_order WHERE imgid=?1))",
232 actual_imgid, expected_imgid);
233
234 if(!diff) return 0;
235
236 test_fail(scenario, "module order differs from expected XMP", failure_reason);
237 print_module_order_summary("actual", actual_imgid);
238 print_module_order_summary("expected", expected_imgid);
239 return 1;
240}
241
242static void print_enabled_state_summary(const char *label, const int32_t imgid)
243{
244 fprintf(stderr, " %s enabled state for image %d:\n", label, imgid);
245
246 sqlite3_stmt *stmt = NULL;
247 sqlite3_prepare_v2(
249 "SELECT h.operation, h.multi_priority, IFNULL(h.multi_name, ''), h.enabled"
250 " FROM main.history h"
251 " WHERE h.imgid=?1"
252 " AND h.num < (SELECT history_end FROM main.images WHERE id=?1)"
253 " AND h.num = (SELECT MAX(h2.num)"
254 " FROM main.history h2"
255 " WHERE h2.imgid=?1"
256 " AND h2.operation=h.operation"
257 " AND h2.multi_priority=h.multi_priority"
258 " AND h2.num < (SELECT history_end FROM main.images WHERE id=?1))"
259 " ORDER BY h.operation, h.multi_priority",
260 -1, &stmt, NULL);
261 sqlite3_bind_int(stmt, 1, imgid);
262
263 while(sqlite3_step(stmt) == SQLITE_ROW)
264 {
265 const unsigned char *operation = sqlite3_column_text(stmt, 0);
266 const unsigned char *multi_name = sqlite3_column_text(stmt, 2);
267 fprintf(stderr, " %-24s priority=%d name='%s' enabled=%d\n",
268 operation ? (const char *)operation : "", sqlite3_column_int(stmt, 1),
269 multi_name ? (const char *)multi_name : "", sqlite3_column_int(stmt, 3));
270 }
271
272 sqlite3_finalize(stmt);
273}
274
275static int compare_enabled_state(const char *scenario, const int32_t actual_imgid,
276 const int32_t expected_imgid, char **failure_reason)
277{
278 const int diff = sql_int_for_bound_images(
279 "WITH actual AS ("
280 " SELECT h.operation, h.multi_priority, IFNULL(h.multi_name, '') AS multi_name, h.enabled"
281 " FROM main.history h"
282 " WHERE h.imgid=?1"
283 " AND h.num < (SELECT history_end FROM main.images WHERE id=?1)"
284 " AND h.num = (SELECT MAX(h2.num)"
285 " FROM main.history h2"
286 " WHERE h2.imgid=?1"
287 " AND h2.operation=h.operation"
288 " AND h2.multi_priority=h.multi_priority"
289 " AND h2.num < (SELECT history_end FROM main.images WHERE id=?1))"
290 "), expected AS ("
291 " SELECT h.operation, h.multi_priority, IFNULL(h.multi_name, '') AS multi_name, h.enabled"
292 " FROM main.history h"
293 " WHERE h.imgid=?2"
294 " AND h.num < (SELECT history_end FROM main.images WHERE id=?2)"
295 " AND h.num = (SELECT MAX(h2.num)"
296 " FROM main.history h2"
297 " WHERE h2.imgid=?2"
298 " AND h2.operation=h.operation"
299 " AND h2.multi_priority=h.multi_priority"
300 " AND h2.num < (SELECT history_end FROM main.images WHERE id=?2))"
301 ")"
302 "SELECT"
303 " (SELECT COUNT(*) FROM (SELECT * FROM actual EXCEPT SELECT * FROM expected))"
304 " +"
305 " (SELECT COUNT(*) FROM (SELECT * FROM expected EXCEPT SELECT * FROM actual))",
306 actual_imgid, expected_imgid);
307
308 if(!diff) return 0;
309
310 test_fail(scenario, "final enabled states differ from expected XMP", failure_reason);
311 print_enabled_state_summary("actual", actual_imgid);
312 print_enabled_state_summary("expected", expected_imgid);
313 return 1;
314}
315
316static int apply_style_to_image(const char *scenario, const char *style_name, const int32_t imgid,
317 char **failure_reason)
318{
319 const int style_id = dt_styles_get_id_by_name(style_name);
320 if(style_id == 0)
321 return test_fail(scenario, "imported style is missing from the database", failure_reason);
322
323 dt_conf_set_bool("history/copy_iop_order", FALSE);
324 dt_conf_set_bool("history/paste_instances", TRUE);
325
326 return dt_styles_apply_to_image_merge(style_name, style_id, imgid, DT_HISTORY_MERGE_APPEND, NULL)
327 ? test_fail(scenario, "style application failed", failure_reason)
328 : 0;
329}
330
331static int run_style_scenario(const char *scenario_dir, const char *source_image_path, const char *style_file,
332 char **failure_reason)
333{
334 char *scenario = scenario_name_from_style_file(style_file);
335 char *style_path = g_build_filename(scenario_dir, style_file, NULL);
336 char *start_xmp_name = g_strdup_printf("start_%s.xmp", scenario);
337 char *end_xmp_name = g_strdup_printf("end_%s.xmp", scenario);
338 char *start_xmp_path = g_build_filename(scenario_dir, start_xmp_name, NULL);
339 char *end_xmp_path = g_build_filename(scenario_dir, end_xmp_name, NULL);
340
341 printf("\n[STEP] %s\n", scenario);
342
343 int result = 1;
344 const int before_import_style_id = max_style_id();
345 dt_styles_import_from_file(style_path);
346 char *style_name = imported_style_name(before_import_style_id);
347 if(IS_NULL_PTR(style_name)) style_name = g_strdup(scenario);
348
349 const int32_t actual_imgid = create_test_image(source_image_path);
350 const int32_t expected_imgid = create_test_image(source_image_path);
351
352 if(actual_imgid <= 0 || expected_imgid <= 0)
353 {
354 test_fail(scenario, "could not import test image", failure_reason);
355 goto end;
356 }
357
358 if(load_start_history(scenario, actual_imgid, start_xmp_path, start_xmp_name, failure_reason)) goto end;
359 if(apply_style_to_image(scenario, style_name, actual_imgid, failure_reason)) goto end;
360 if(load_xmp_on_image(scenario, expected_imgid, end_xmp_path, failure_reason)) goto end;
361 if(compare_pipe_order(scenario, actual_imgid, expected_imgid, failure_reason)) goto end;
362 if(compare_enabled_state(scenario, actual_imgid, expected_imgid, failure_reason)) goto end;
363
364 printf("[OK] %s\n", scenario);
365 result = 0;
366
367end:
368 dt_free(style_name);
369 dt_free(scenario);
370 dt_free(style_path);
371 dt_free(start_xmp_name);
372 dt_free(end_xmp_name);
373 dt_free(start_xmp_path);
374 dt_free(end_xmp_path);
375 return result;
376}
377
378static int run_style_scenarios(const char *scenario_dir, const char *source_image_path)
379{
380 GError *error = NULL;
381 GDir *dir = g_dir_open(scenario_dir, 0, &error);
382 if(IS_NULL_PTR(dir))
383 {
384 fprintf(stderr, "[FAIL] cannot open style scenario folder `%s`: %s\n", scenario_dir,
385 error ? error->message : "unknown error");
386 g_clear_error(&error);
387 return 1;
388 }
389
390 GList *style_files = NULL;
391 const char *entry = NULL;
392 while((entry = g_dir_read_name(dir)) != NULL)
393 {
394 if(is_style_file(entry)) style_files = g_list_prepend(style_files, g_strdup(entry));
395 }
396 g_dir_close(dir);
397
398 style_files = g_list_sort(style_files, (GCompareFunc)g_strcmp0);
399 if(IS_NULL_PTR(style_files))
400 {
401 printf("[SKIP] no .dtstyle file found in %s\n", scenario_dir);
402 return 0;
403 }
404
405 int result = 0;
406 GList *summaries = NULL;
407 for(GList *l = style_files; l; l = g_list_next(l))
408 {
409 const char *style_file = (const char *)l->data;
410 dt_style_scenario_result_t *summary = g_malloc0(sizeof(*summary));
411 summary->style_file = g_strdup(style_file);
412 summary->result = run_style_scenario(scenario_dir, source_image_path, style_file, &summary->reason);
413 if(summary->result && IS_NULL_PTR(summary->reason))
414 summary->reason = g_strdup("unknown failure");
415
416 summaries = g_list_prepend(summaries, summary);
417 result |= summary->result;
418 }
419
420 summaries = g_list_reverse(summaries);
421 printf("\n[SUMMARY]\n");
422 for(GList *l = summaries; l; l = g_list_next(l))
423 {
425 printf("%s: %s", summary->style_file, summary->result ? "FAILED" : "PASSED");
426 if(summary->result)
427 printf(" - %s", summary->reason);
428 printf("\n");
429 }
430
431 for(GList *l = summaries; l; l = g_list_next(l))
432 {
434 dt_free(summary->style_file);
435 dt_free(summary->reason);
436 dt_free(summary);
437 }
438 g_list_free(summaries);
439 g_list_free_full(style_files, dt_free_gpointer);
440 return result;
441}
442
443static char *prepare_test_datadir(const char *tmp_dir)
444{
445 char *datadir = g_build_filename(tmp_dir, "data", NULL);
446 char *rawspeed_dir = g_build_filename(datadir, "rawspeed", NULL);
447 char *rawspeed_xml = g_build_filename(rawspeed_dir, "cameras.xml", NULL);
448 char *rawspeed_source_xml = g_build_filename(ANSEL_TEST_SOURCE_DIR, "src", "external", "rawspeed", "data",
449 "cameras.xml", NULL);
450
451 g_mkdir(datadir, 0700);
452 g_mkdir(rawspeed_dir, 0700);
453
454 GError *error = NULL;
455 GFile *source_file = g_file_new_for_path(rawspeed_source_xml);
456 GFile *dest_file = g_file_new_for_path(rawspeed_xml);
457 const gboolean copied = g_file_copy(source_file, dest_file, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error);
458 g_object_unref(source_file);
459 g_object_unref(dest_file);
460
461 if(!copied)
462 {
463 fprintf(stderr, "[FAIL] copy rawspeed cameras.xml: %s\n", error ? error->message : "unknown error");
464 g_clear_error(&error);
465 dt_free(datadir);
466 datadir = NULL;
467 }
468
469 dt_free(rawspeed_dir);
470 dt_free(rawspeed_xml);
471 dt_free(rawspeed_source_xml);
472 return datadir;
473}
474
475int main(int argc, char *argv[])
476{
477 const gboolean explicit_scenario_dir = argc > 1;
478 char *default_scenario_dir = g_build_filename(ANSEL_TEST_SOURCE_DIR, "tests", "styles", NULL);
479 char *default_source_image = g_build_filename(ANSEL_TEST_SOURCE_DIR, "tests", "integration", "images",
480 "mire1.cr2", NULL);
481 const char *scenario_dir = explicit_scenario_dir ? argv[1] : default_scenario_dir;
482 const char *source_image_path = (argc > 2) ? argv[2] : default_source_image;
483
484 if(!g_file_test(scenario_dir, G_FILE_TEST_IS_DIR))
485 {
486 fprintf(stderr, explicit_scenario_dir ? "[FAIL] style scenario folder does not exist: %s\n"
487 : "[SKIP] style scenario folder does not exist: %s\n",
488 scenario_dir);
489 dt_free(default_scenario_dir);
490 dt_free(default_source_image);
491 return explicit_scenario_dir ? 1 : 0;
492 }
493
494 if(!g_file_test(source_image_path, G_FILE_TEST_IS_REGULAR))
495 {
496 fprintf(stderr, "[FAIL] test image does not exist: %s\n", source_image_path);
497 dt_free(default_scenario_dir);
498 dt_free(default_source_image);
499 return 1;
500 }
501
502 char *config_dir = g_strdup_printf("%s/ansel-test-styles-config-XXXXXX", g_get_tmp_dir());
503 char *cache_dir = g_strdup_printf("%s/ansel-test-styles-cache-XXXXXX", g_get_tmp_dir());
504 char *tmp_dir = g_strdup_printf("%s/ansel-test-styles-tmp-XXXXXX", g_get_tmp_dir());
505
506 assert(!IS_NULL_PTR(g_mkdtemp(config_dir)));
507 assert(!IS_NULL_PTR(g_mkdtemp(cache_dir)));
508 assert(!IS_NULL_PTR(g_mkdtemp(tmp_dir)));
509 test_image_dir = tmp_dir;
510
511 char *test_datadir = prepare_test_datadir(tmp_dir);
512 if(IS_NULL_PTR(test_datadir))
513 {
514 dt_free(default_scenario_dir);
515 dt_free(default_source_image);
516 dt_free(config_dir);
517 dt_free(cache_dir);
518 dt_free(tmp_dir);
519 return 1;
520 }
521
522 char *noiseprofiles = g_build_filename(ANSEL_TEST_SOURCE_DIR, "data", "noiseprofiles.json", NULL);
523 char *argv_override[] = {
524 "ansel-test-styles",
525 "--library", ":memory:",
526 "--datadir", test_datadir,
527 "--noiseprofiles", noiseprofiles,
528 "--moduledir", ANSEL_TEST_BINARY_DIR "/lib/ansel",
529 "--configdir", config_dir,
530 "--cachedir", cache_dir,
531 "--tmpdir", tmp_dir,
532 "--disable-opencl",
533 "--conf", "write_sidecar_files=FALSE",
534 "--conf", "plugins/lighttable/export/force_lcms2=FALSE",
535 "-t", "1",
536 NULL
537 };
538 int argc_override = sizeof(argv_override) / sizeof(*argv_override) - 1;
539
540 if(dt_init(argc_override, argv_override, FALSE, FALSE)) exit(1);
541
542 const int result = run_style_scenarios(scenario_dir, source_image_path);
543
544 dt_cleanup();
545 dt_free(noiseprofiles);
546 dt_free(test_datadir);
547 dt_free(default_scenario_dir);
548 dt_free(default_source_image);
549 dt_free(config_dir);
550 dt_free(cache_dir);
551 dt_free(tmp_dir);
552
553 return result;
554}
555
556// clang-format off
557// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
558// vim: shiftwidth=2 expandtab tabstop=2 cindent
559// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
560// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
const char * extension(dt_imageio_module_data_t *data)
Definition avif.c:651
void dt_conf_set_bool(const char *name, int val)
int32_t dt_image_import(const int32_t film_id, const char *filename, gboolean raise_signals)
void dt_cleanup()
Definition darktable.c:1864
int dt_init(int argc, char *argv[], const gboolean init_gui, const gboolean load_data)
Definition darktable.c:885
sqlite3 * dt_database_get_sqlite3_global(void)
Definition database.c:3782
void dt_film_init(dt_film_t *film)
Definition film.c:74
int dt_film_new(dt_film_t *film, const char *directory)
Definition film.c:129
void dt_film_cleanup(dt_film_t *film)
Definition film.c:84
int dt_history_load_and_apply(const int32_t imgid, gchar *filename, int history_only)
@ DT_HISTORY_MERGE_APPEND
#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
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
const char * name
Definition pdf.h:90
int main()
Definition prova.c:47
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
int dt_styles_apply_to_image_merge(const char *name, const int style_id, const int32_t newimgid, const dt_history_merge_strategy_t mode, dt_hm_batch_state_t *batch)
int32_t dt_styles_get_id_by_name(const char *name)
void dt_styles_import_from_file(const char *style_path)
int32_t id
Definition film.h:45
One-line scenario result printed after every style has run.
static int test_fail(const char *scenario, const char *message, char **failure_reason)
static int sql_int_for_bound_images(const char *query, const int32_t imgid_a, const int32_t imgid_b)
static char * prepare_test_datadir(const char *tmp_dir)
static int load_start_history(const char *scenario, const int32_t imgid, const char *start_xmp_path, const char *start_xmp_name, char **failure_reason)
static char * imported_style_name(const int before_import_style_id)
static char * test_image_dir
Run style-application scenarios from tests/styles.
static int compare_enabled_state(const char *scenario, const int32_t actual_imgid, const int32_t expected_imgid, char **failure_reason)
static int run_style_scenarios(const char *scenario_dir, const char *source_image_path)
#define ANSEL_TEST_SOURCE_DIR
static int32_t create_test_image(const char *source_image_path)
static int max_style_id(void)
static char * scenario_name_from_style_file(const char *filename)
static int compare_pipe_order(const char *scenario, const int32_t actual_imgid, const int32_t expected_imgid, char **failure_reason)
static int apply_style_to_image(const char *scenario, const char *style_name, const int32_t imgid, char **failure_reason)
static gboolean is_style_file(const char *filename)
static int run_style_scenario(const char *scenario_dir, const char *source_image_path, const char *style_file, char **failure_reason)
static int load_xmp_on_image(const char *scenario, const int32_t imgid, const char *xmp_path, char **failure_reason)
static void print_module_order_summary(const char *label, const int32_t imgid)
static void print_enabled_state_summary(const char *label, const int32_t imgid)
#define ANSEL_TEST_BINARY_DIR