summaryrefslogtreecommitdiff
path: root/tests/test_loader_pixbuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_loader_pixbuf.c')
-rw-r--r--tests/test_loader_pixbuf.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/tests/test_loader_pixbuf.c b/tests/test_loader_pixbuf.c
new file mode 100644
index 0000000..ba37522
--- /dev/null
+++ b/tests/test_loader_pixbuf.c
@@ -0,0 +1,163 @@
+/*:*
+ * ggaze — GdkPixbuf loader backend unit test
+ *
+ * Loads committed fixtures via loader_load() and asserts the resulting
+ * GdkTexture dimensions, including the rotated-EXIF case (decision #26): an
+ * 8x4 JPEG with Orientation=6 must load as 4x8 after
+ * gdk_pixbuf_apply_embedded_orientation. No display needed (texture creation
+ * from a pixbuf is headless). Fixture dir comes from $GGAZE_FIXTURES_DIR
+ * (set by meson). See ./sample-images for the optional realistic corpus.
+ *
+ * Copyright (c) 2026 ggaze contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *:*/
+
+#include "loader/loader.h"
+
+#include <gdk/gdk.h>
+#include <gio/gio.h>
+#include <glib.h>
+#include <unistd.h>
+
+static GdkTexture *
+load_fixture(const gchar *c_name) {
+ const gchar *c_dir = g_getenv("GGAZE_FIXTURES_DIR");
+ g_assert_nonnull(c_dir);
+ gchar *c_path = g_build_filename(c_dir, c_name, NULL);
+ GFile *p_file = g_file_new_for_path(c_path);
+ GError *p_err = NULL;
+ GdkTexture *p_tex = loader_load(p_file, NULL, &p_err);
+ g_assert_no_error(p_err);
+ g_object_unref(p_file);
+ g_free(c_path);
+ return (p_tex);
+}
+
+static void
+test_plain_jpeg(void) {
+ /* 6x3, Orientation = 1 -> 6x3 (no rotation applied). */
+ GdkTexture *p_tex = load_fixture("plain.jpg");
+ g_assert_cmpint(gdk_texture_get_width(p_tex), ==, 6);
+ g_assert_cmpint(gdk_texture_get_height(p_tex), ==, 3);
+ g_object_unref(p_tex);
+}
+
+static void
+test_rotated_exif_jpeg(void) {
+ /* 8x4, Orientation = 6 (rotate 90 CW) -> upright 4x8 (decision #26). */
+ GdkTexture *p_tex = load_fixture("rot6.jpg");
+ g_assert_cmpint(gdk_texture_get_width(p_tex), ==, 4);
+ g_assert_cmpint(gdk_texture_get_height(p_tex), ==, 8);
+ g_object_unref(p_tex);
+}
+
+static void
+test_png(void) {
+ /* 5x2 PNG, no orientation. */
+ GdkTexture *p_tex = load_fixture("small.png");
+ g_assert_cmpint(gdk_texture_get_width(p_tex), ==, 5);
+ g_assert_cmpint(gdk_texture_get_height(p_tex), ==, 2);
+ g_object_unref(p_tex);
+}
+
+static void
+test_missing_file_errors(void) {
+ const gchar *c_dir = g_getenv("GGAZE_FIXTURES_DIR");
+ g_assert_nonnull(c_dir);
+ gchar *c_path = g_build_filename(c_dir, "does-not-exist.jpg", NULL);
+ GFile *p_file = g_file_new_for_path(c_path);
+ GError *p_err = NULL;
+ GdkTexture *p_tex = loader_load(p_file, NULL, &p_err);
+ g_assert_null(p_tex);
+ g_assert_nonnull(p_err);
+ g_error_free(p_err);
+ g_object_unref(p_file);
+ g_free(c_path);
+}
+
+/* Write raw bytes to a temp file and load them (magic-byte / corrupt cases). */
+static GdkTexture *
+load_bytes(const guint8 *p_buf, gsize u_len, GError **p_err) {
+ gchar *c_path = NULL;
+ GError *p_sub = NULL;
+ gint i_fd = g_file_open_tmp("ggaze-XXXXXX", &c_path, &p_sub);
+ g_assert_no_error(p_sub);
+ g_assert_cmpint(i_fd, >=, 0);
+ gsize u_off = 0;
+ while (u_off < u_len) {
+ gssize n = write(i_fd, p_buf + u_off, u_len - u_off);
+ g_assert_cmpint(n, >, 0);
+ u_off += (gsize)n;
+ }
+ close(i_fd);
+ GFile *p_file = g_file_new_for_path(c_path);
+ GdkTexture *p_tex = loader_load(p_file, NULL, p_err);
+ g_object_unref(p_file);
+ unlink(c_path);
+ g_free(c_path);
+ return (p_tex);
+}
+
+static void
+assert_unsupported(const guint8 *p_buf, gsize u_len) {
+ GError *p_err = NULL;
+ GdkTexture *p_tex = load_bytes(p_buf, u_len, &p_err);
+ g_assert_null(p_tex);
+ g_assert_nonnull(p_err);
+ g_assert_cmpint(p_err->code, ==, G_IO_ERROR_NOT_SUPPORTED);
+ g_error_free(p_err);
+}
+
+static void
+test_unsupported_jxl(void) {
+ const guint8 h[] = {0xFF, 0x0A, 0x10, 0x00}; /* JXL codestream magic */
+ assert_unsupported(h, G_N_ELEMENTS(h));
+}
+
+static void
+test_unsupported_avif(void) {
+ const guint8 h[] = {0, 0, 0, 0, 'f', 't', 'y', 'p', 'a', 'v', 'i', 'f'};
+ assert_unsupported(h, G_N_ELEMENTS(h));
+}
+
+static void
+test_unsupported_heif(void) {
+ const guint8 h[] = {0, 0, 0, 0, 'f', 't', 'y', 'p', 'h', 'e', 'i', 'c'};
+ assert_unsupported(h, G_N_ELEMENTS(h));
+}
+
+static void
+test_corrupt_jpeg(void) {
+ /* Truncated JPEG: SOI + APP0 marker, no image data. GdkPixbuf fails. */
+ const guint8 h[] = {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 'J', 'F',
+ 'I', 'F', 0, 1, 1, 0, 0, 0};
+ GError *p_err = NULL;
+ GdkTexture *p_tex = load_bytes(h, G_N_ELEMENTS(h), &p_err);
+ g_assert_null(p_tex);
+ g_assert_nonnull(p_err);
+ g_error_free(p_err);
+}
+
+static void
+test_rgba_png(void) {
+ /* 5x2 RGBA PNG -> exercises the has-alpha branch of texture_from_pixbuf. */
+ GdkTexture *p_tex = load_fixture("rgba.png");
+ g_assert_cmpint(gdk_texture_get_width(p_tex), ==, 5);
+ g_assert_cmpint(gdk_texture_get_height(p_tex), ==, 2);
+ g_object_unref(p_tex);
+}
+
+int
+main(int i_argc, char **c_argv) {
+ g_test_init(&i_argc, &c_argv, NULL);
+ g_test_add_func("/loader/pixbuf/plain_jpeg", test_plain_jpeg);
+ g_test_add_func("/loader/pixbuf/rotated_exif", test_rotated_exif_jpeg);
+ g_test_add_func("/loader/pixbuf/png", test_png);
+ g_test_add_func("/loader/pixbuf/missing_file", test_missing_file_errors);
+ g_test_add_func("/loader/pixbuf/unsupported_jxl", test_unsupported_jxl);
+ g_test_add_func("/loader/pixbuf/unsupported_avif", test_unsupported_avif);
+ g_test_add_func("/loader/pixbuf/unsupported_heif", test_unsupported_heif);
+ g_test_add_func("/loader/pixbuf/corrupt_jpeg", test_corrupt_jpeg);
+ g_test_add_func("/loader/pixbuf/rgba_png", test_rgba_png);
+ return (g_test_run());
+} \ No newline at end of file