summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-07-12 17:30:26 +0300
committerPaul Buetow <paul@buetow.org>2026-07-12 17:30:26 +0300
commit547b810eea19b0c72731e30ca3e2e339593c1833 (patch)
treee19fc9308d2f93ffd1d558ec705effc6d9ffb752 /tests
parent1770b12d5d95476310c27ccf39a91b5ba4a3f119 (diff)
skeleton: GtkApplication, window, GSettings schema, desktop, tests (ft0)
M0 (task ft0): the app shell — no feature views yet (those are M1 large, M7 grid). - src/main.c: g_application_run + --version via handle-local-options (--help is GApplication's own); exits before activate so --version works headless. - src/app.{c,h}: GgazeApp : AdwApplication : GtkApplication (libadwaita styling, decision #29), single-instance (decision #32), G_APPLICATION_HANDLES_OPEN; activate + open reuse the active window (replace-on-new-open); ggaze_window_open remembers the GFile. - src/window.{c,h}: GgazeWindow : GtkApplicationWindow with AdwHeaderBar and a GtkStack (children 'grid'/'large', default 'grid'); dispose clears the file. - data/org.buetow.ggaze.desktop (image/* MIME, validated) and data/org.buetow.ggaze.gschema.xml (all keys + defaults, --strict clean). - meson: libggaze static lib shared by the executable and tests; install to bindir/applications/glib-2.0 schemas; gnome.post_install compiles schemas + updates desktop db. - tests: unit test_app (--version/--help/unknown via subprocess), integration test_window (offscreen stack checks + open-titles), bootstrap. 3/3 green. - docs/tech-stack.md: window-geometry shape corrected to (width,height, fullscreen,maximized) to match the schema.
Diffstat (limited to 'tests')
-rw-r--r--tests/meson.build26
-rw-r--r--tests/test_app.c80
-rw-r--r--tests/test_window.c80
3 files changed, 186 insertions, 0 deletions
diff --git a/tests/meson.build b/tests/meson.build
index c0ec9fb..4c02ccd 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -10,6 +10,7 @@
# meson test -C build
# ninja -C build coverage (needs lcov + genhtml)
+# --- unit track ---
test_bootstrap = executable(
'test_bootstrap',
['test_bootstrap.c', ggaze_conf_h],
@@ -19,4 +20,29 @@ test_bootstrap = executable(
)
test('bootstrap', test_bootstrap, suite : 'unit')
+# test_app runs the ggaze binary as a subprocess; meson passes its path.
+test_app = executable(
+ 'test_app',
+ ['test_app.c', ggaze_conf_h],
+ include_directories : inc,
+ dependencies : [glib_dep, gio_dep],
+ install : false,
+)
+test('app', test_app,
+ suite : 'unit',
+ args : [ggaze_exe.full_path()],
+ depends : ggaze_exe,
+)
+
+# --- integration track (needs a display; CI uses xvfb-run) ---
+test_window = executable(
+ 'test_window',
+ ['test_window.c', ggaze_conf_h],
+ include_directories : [inc, src_inc],
+ dependencies : ggaze_deps,
+ link_with : ggaze_lib,
+ install : false,
+)
+test('window', test_window, suite : 'integration')
+
subdir('integration') \ No newline at end of file
diff --git a/tests/test_app.c b/tests/test_app.c
new file mode 100644
index 0000000..b1438a1
--- /dev/null
+++ b/tests/test_app.c
@@ -0,0 +1,80 @@
+/*:*
+ * ggaze — app CLI unit test
+ *
+ * Exercises the ggaze binary as a subprocess (so --help/--version, which call
+ * exit() inside g_application_run, do not terminate the test process itself).
+ * Verifies: `--version` prints "ggaze" and exits 0; `--help` exits 0; an
+ * unknown flag exits non-zero. No display is needed — all three exit before
+ * activate. The ggaze binary path is passed as argv[1] by meson.
+ *
+ * Copyright (c) 2026 ggaze contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *:*/
+
+#include "ggaze-config.h"
+
+#include <glib.h>
+#include <gio/gio.h>
+
+static const char *GGAZE_BIN;
+
+/* Run GGAZE_BIN with c_arg, return the exit status (0 on success). If
+ * c_out is non-NULL, receive the combined stdout+stderr. */
+static gint
+run_one(const char *c_arg, char **c_out) {
+ GError *p_err = NULL;
+ GSubprocess *p_sub = g_subprocess_new(G_SUBPROCESS_FLAGS_STDOUT_PIPE |
+ G_SUBPROCESS_FLAGS_STDERR_MERGE,
+ &p_err, GGAZE_BIN, c_arg, NULL);
+ g_assert_no_error(p_err);
+
+ char *c_stdout = NULL;
+ g_subprocess_communicate_utf8(p_sub, NULL, NULL, &c_stdout, NULL, &p_err);
+ g_assert_no_error(p_err);
+
+ g_assert_true(g_subprocess_get_if_exited(p_sub));
+ gint i_status = g_subprocess_get_exit_status(p_sub);
+
+ if (c_out != NULL) {
+ *c_out = c_stdout;
+ } else {
+ g_free(c_stdout);
+ }
+ g_object_unref(p_sub);
+ return (i_status);
+}
+
+static void
+test_version(void) {
+ char *c_out = NULL;
+ gint i_status = run_one("--version", &c_out);
+ g_assert_cmpint(i_status, ==, 0);
+ g_assert_nonnull(c_out);
+ g_assert_nonnull(g_strstr_len(c_out, -1, "ggaze"));
+ g_free(c_out);
+}
+
+static void
+test_help(void) {
+ /* GApplication provides --help itself; it prints and exits 0. */
+ gint i_status = run_one("--help", NULL);
+ g_assert_cmpint(i_status, ==, 0);
+}
+
+static void
+test_unknown_flag(void) {
+ gint i_status = run_one("--definitely-not-a-ggaze-flag", NULL);
+ g_assert_cmpint(i_status, !=, 0);
+}
+
+int
+main(int i_argc, char **c_argv) {
+ g_assert_cmpint(i_argc, >=, 2);
+ GGAZE_BIN = c_argv[1];
+
+ g_test_init(&i_argc, &c_argv, NULL);
+ g_test_add_func("/app/version", test_version);
+ g_test_add_func("/app/help", test_help);
+ g_test_add_func("/app/unknown_flag", test_unknown_flag);
+ return (g_test_run());
+} \ No newline at end of file
diff --git a/tests/test_window.c b/tests/test_window.c
new file mode 100644
index 0000000..0570397
--- /dev/null
+++ b/tests/test_window.c
@@ -0,0 +1,80 @@
+/*:*
+ * ggaze — window smoke test (integration)
+ *
+ * Constructs a GgazeWindow offscreen and asserts the two-view stack exists
+ * (children "grid" and "large", default visible = "grid"), and that
+ * ggaze_window_open titles the window with the file's basename.
+ *
+ * The window is built with g_object_new() (no "application" property): the
+ * stack/header are constructed in ggaze_window_init, independent of the app
+ * association. Setting GtkWindow:application requires the GApplication
+ * ::startup signal to have fired (it does in production via activate/open;
+ * not in this isolated smoke test). Needs a display, so this lives in the
+ * `integration` suite (CI runs it under xvfb); skipped cleanly when no
+ * display is available.
+ *
+ * Copyright (c) 2026 ggaze contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *:*/
+
+#include "window.h"
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+
+static GgazeWindow *
+new_window(void) {
+ return (GGAZE_WINDOW(g_object_new(GGAZE_TYPE_WINDOW, NULL)));
+}
+
+static void
+test_stack_has_two_views(void) {
+ GgazeWindow *p_win = new_window();
+ g_assert_nonnull(p_win);
+
+ GtkWidget *p_child = gtk_window_get_child(GTK_WINDOW(p_win));
+ g_assert_nonnull(p_child);
+ g_assert_true(GTK_IS_STACK(p_child));
+
+ GtkStack *p_stack = GTK_STACK(p_child);
+ GListModel *p_pages = G_LIST_MODEL(gtk_stack_get_pages(p_stack));
+ g_assert_cmpint(g_list_model_get_n_items(p_pages), ==, 2);
+ g_assert_nonnull(gtk_stack_get_child_by_name(p_stack, "grid"));
+ g_assert_nonnull(gtk_stack_get_child_by_name(p_stack, "large"));
+ g_assert_cmpstr(gtk_stack_get_visible_child_name(p_stack), ==, "grid");
+
+ g_object_unref(p_win);
+}
+
+static void
+test_open_titles_window(void) {
+ GgazeWindow *p_win = new_window();
+
+ GFile *p_file = g_file_new_for_path("/tmp/IMG_0001.jpg");
+ ggaze_window_open(p_win, p_file);
+
+ g_assert_cmpstr(gtk_window_get_title(GTK_WINDOW(p_win)), ==, "IMG_0001.jpg");
+
+ g_object_unref(p_file);
+ g_object_unref(p_win);
+}
+
+int
+main(int i_argc, char **c_argv) {
+ g_test_init(&i_argc, &c_argv, NULL);
+
+ /* Tolerate host GTK WARNINGs (e.g. an unknown gtk-modules key delivered
+ * by the live GNOME session via XSettings) that g_test_init makes fatal.
+ * Keep CRITICALs fatal — those are real bugs. */
+ g_log_set_always_fatal(G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
+
+ if (!gtk_init_check()) {
+ g_test_skip("no display available (run under xvfb)");
+ return (g_test_run());
+ }
+
+ g_test_add_func("/window/stack_has_two_views", test_stack_has_two_views);
+ g_test_add_func("/window/open_titles_window", test_open_titles_window);
+ return (g_test_run());
+} \ No newline at end of file