11c235774Smrg/*
21c235774Smrg * Copyright (c) 2023, Oracle and/or its affiliates.
31c235774Smrg *
41c235774Smrg * Permission is hereby granted, free of charge, to any person obtaining a
51c235774Smrg * copy of this software and associated documentation files (the "Software"),
61c235774Smrg * to deal in the Software without restriction, including without limitation
71c235774Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
81c235774Smrg * and/or sell copies of the Software, and to permit persons to whom the
91c235774Smrg * Software is furnished to do so, subject to the following conditions:
101c235774Smrg *
111c235774Smrg * The above copyright notice and this permission notice (including the next
121c235774Smrg * paragraph) shall be included in all copies or substantial portions of the
131c235774Smrg * Software.
141c235774Smrg *
151c235774Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
161c235774Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
171c235774Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
181c235774Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
191c235774Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
201c235774Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
211c235774Smrg * DEALINGS IN THE SOFTWARE.
221c235774Smrg */
231c235774Smrg
241c235774Smrg#include <glib.h>
251c235774Smrg
261c235774Smrg#include <errno.h>
271c235774Smrg#include <limits.h>
281c235774Smrg#include <setjmp.h>
291c235774Smrg#include <signal.h>
301c235774Smrg#include <stdlib.h>
311c235774Smrg#include <unistd.h>
321c235774Smrg
331c235774Smrg#include "config.h"
341c235774Smrg
3552dc082bSmrg/* g_pattern_spec_match_string is available in glib 2.70 and later,
3652dc082bSmrg   to replace the deprecated g_pattern_match_string */
3752dc082bSmrg#ifdef HAVE_G_PATTERN_SPEC_MATCH_STRING
3852dc082bSmrg#define g_pattern_match_string g_pattern_spec_match_string
3952dc082bSmrg#endif
4052dc082bSmrg
411c235774Smrg#define DEFAULT_TIMEOUT 10 /* maximum seconds for each file */
421c235774Smrg
431c235774Smrgstatic sigjmp_buf jump_env;
441c235774Smrg
451c235774Smrgstatic void sigalrm (int sig)
461c235774Smrg{
471c235774Smrg    siglongjmp(jump_env, 1);
481c235774Smrg}
491c235774Smrg
501c235774Smrgtypedef int (*testfilefunc)(const gchar *filepath);
511c235774Smrg
521c235774Smrg/*
531c235774Smrg * Test all files in a given subdir of either the build or source directory
541c235774Smrg */
551c235774Smrgstatic void
561c235774SmrgTestAllFilesByType(GTestFileType file_type, gboolean compressed,
571c235774Smrg                   const char *subdir, int expected, testfilefunc testfunc)
581c235774Smrg{
591c235774Smrg    const gchar *datadir_path, *filename;
601c235774Smrg    GDir *datadir;
611c235774Smrg    GError *err = NULL;
621c235774Smrg    int timeout = DEFAULT_TIMEOUT;
631c235774Smrg    char *timeout_env;
641c235774Smrg
651c235774Smrg    GPatternSpec *xpm_pattern = g_pattern_spec_new("*.xpm");
661c235774Smrg#ifndef NO_ZPIPE
671c235774Smrg    GPatternSpec *z_pattern = compressed ? g_pattern_spec_new("*.xpm.Z") : NULL;
681c235774Smrg    GPatternSpec *gz_pattern = compressed ? g_pattern_spec_new("*.xpm.gz") : NULL;
691c235774Smrg#endif
701c235774Smrg
711c235774Smrg    /* Allow override when debugging tests */
721c235774Smrg    timeout_env = getenv("XPM_TEST_TIMEOUT");
731c235774Smrg    if (timeout_env != NULL) {
741c235774Smrg        int from_env = atoi(timeout_env);
751c235774Smrg
761c235774Smrg        if (from_env >= 0)
771c235774Smrg            timeout = from_env;
781c235774Smrg    }
791c235774Smrg
801c235774Smrg    datadir_path = g_test_get_filename(file_type, "pixmaps", subdir,
811c235774Smrg                       (file_type == G_TEST_BUILT) ? "generated" : NULL, NULL);
821c235774Smrg    g_assert_nonnull(datadir_path);
831c235774Smrg    g_test_message("Reading files from %s", datadir_path);
841c235774Smrg
851c235774Smrg    datadir = g_dir_open(datadir_path, 0, &err);
861c235774Smrg    g_assert_no_error(err);
871c235774Smrg
881c235774Smrg    errno = 0;
891c235774Smrg    while ((filename = g_dir_read_name(datadir)) != NULL) {
901c235774Smrg
911c235774Smrg        if (!g_pattern_match_string(xpm_pattern, filename)) {
921c235774Smrg#ifndef NO_ZPIPE
931c235774Smrg                if (!compressed ||
941c235774Smrg                    (!g_pattern_match_string(z_pattern, filename) &&
951c235774Smrg                     !g_pattern_match_string(gz_pattern, filename)))
961c235774Smrg#endif
971c235774Smrg                {
981c235774Smrg                    g_test_message("skipping \"%s\"", filename);
991c235774Smrg                    continue;
1001c235774Smrg                }
1011c235774Smrg        }
1021c235774Smrg
1031c235774Smrg        /*
1041c235774Smrg         * Assumes the test function should complete in less than "timeout"
1051c235774Smrg         * seconds and fails if they don't, in order to catch runaway loops.
1061c235774Smrg         */
1071c235774Smrg        if (timeout > 0) {
1081c235774Smrg            struct sigaction sa = {
1091c235774Smrg                .sa_handler = sigalrm,
1101c235774Smrg                .sa_flags = SA_RESTART
1111c235774Smrg            };
1121c235774Smrg            sigemptyset (&sa.sa_mask);
1131c235774Smrg            sigaction(SIGALRM, &sa, NULL);
1141c235774Smrg        }
1151c235774Smrg
1161c235774Smrg        if (sigsetjmp(jump_env, 1) == 0) {
1171c235774Smrg            int status;
1181c235774Smrg            gchar *filepath;
1191c235774Smrg
1201c235774Smrg            filepath = g_build_filename(datadir_path, filename, NULL);
1211c235774Smrg
1221c235774Smrg            g_test_message("testing \"%s\", should return %d",
1231c235774Smrg                           filename, expected);
1241c235774Smrg            if (timeout > 0)
1251c235774Smrg                alarm(timeout);
1261c235774Smrg            status = testfunc(filepath);
1271c235774Smrg            g_assert_cmpint(status, ==, expected);
1281c235774Smrg
1291c235774Smrg            if (timeout > 0) {
1301c235774Smrg                status = alarm(0); /* cancel alarm */
1311c235774Smrg                g_test_message("%d seconds left on %d second timer",
1321c235774Smrg                               status, timeout);
1331c235774Smrg            }
1341c235774Smrg
1351c235774Smrg            g_free(filepath);
1361c235774Smrg        }
1371c235774Smrg        else {
1381c235774Smrg            g_test_message("timed out reading %s", filename);
1391c235774Smrg            g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC,
1401c235774Smrg                                "test timed out");
1411c235774Smrg        }
1421c235774Smrg
1431c235774Smrg        errno = 0;
1441c235774Smrg    }
1451c235774Smrg    // g_assert_cmpint(errno, ==, 0); - not sure why this sometimes fails
1461c235774Smrg
1471c235774Smrg    g_dir_close(datadir);
1481c235774Smrg}
1491c235774Smrg
1501c235774Smrg/*
1511c235774Smrg * Test all non-compressed files in a given subdir
1521c235774Smrg */
1531c235774Smrgstatic void
1541c235774SmrgTestAllNormalFiles(const char *subdir, int expected, testfilefunc testfunc)
1551c235774Smrg{
1561c235774Smrg    TestAllFilesByType(G_TEST_DIST, FALSE, subdir, expected, testfunc);
1571c235774Smrg}
1581c235774Smrg
1591c235774Smrg/*
1601c235774Smrg * Test all compressed files in a given subdir
1611c235774Smrg */
1621c235774Smrgstatic void
1631c235774SmrgTestAllCompressedFiles(const char *subdir, int expected, testfilefunc testfunc)
1641c235774Smrg{
16574835918Smrg#ifdef NO_ZPIPE
16674835918Smrg    g_test_message("compression disabled, skipping compressed file tests");
16774835918Smrg#else
1681c235774Smrg    TestAllFilesByType(G_TEST_BUILT, TRUE, subdir, expected, testfunc);
16974835918Smrg#endif
1701c235774Smrg}
171