Edit

kc3-lang/libxkbcommon/test/utils.c

Branch :

  • Show log

    Commit

  • Author : Pierre Le Marre
    Date : 2025-06-06 20:27:45
    Hash : 16c079d6
    Message : chore: Rename is_absolute to is_absolute_path

  • test/utils.c
  • /*
     * Copyright © 2019 Red Hat, Inc.
     * SPDX-License-Identifier: MIT
     */
    
    #include "config.h"
    
    #include <assert.h>
    #include <stdio.h>
    #include <stdint.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    
    #include "test.h"
    #include "utils.h"
    #include "utils-numbers.h"
    #include "utils-paths.h"
    #include "test/utils-text.h"
    
    static void
    test_string_functions(void)
    {
        char buffer[10];
    
        assert(!snprintf_safe(buffer, 0, "foo"));
        assert(!snprintf_safe(buffer, 1, "foo"));
        assert(!snprintf_safe(buffer, 3, "foo"));
    
        assert(snprintf_safe(buffer, 10, "foo"));
        assert(streq(buffer, "foo"));
    
        assert(!snprintf_safe(buffer, 10, "%s", "1234567890"));
        assert(snprintf_safe(buffer, 10, "%s", "123456789"));
    
        assert(streq_null("foo", "foo"));
        assert(!streq_null("foobar", "foo"));
        assert(!streq_null("foobar", NULL));
        assert(!streq_null(NULL, "foobar"));
        assert(streq_null(NULL, NULL));
    
        const char text[] =
            "123; // abc\n"
            "  // def\n"
            "456 // ghi // jkl\n"
            "// mno\n"
            "//\n"
            "ok; // pqr\n"
            "foo\n";
    
        char *out;
        out = strip_lines(text, ARRAY_SIZE(text), "//");
        assert_streq_not_null(
            "strip_lines",
            "123; \n"
            "456 \n"
            "ok; \n"
            "foo\n",
            out
        );
        free(out);
    
        out = uncomment(text, ARRAY_SIZE(text), "//");
        assert_streq_not_null(
            "uncomment",
            "123;  abc\n"
            "   def\n"
            "456  ghi // jkl\n"
            " mno\n"
            "\n"
            "ok;  pqr\n"
            "foo\n",
            out
        );
        free(out);
    }
    
    static void
    test_path_functions(void)
    {
        /* Absolute paths */
        assert(!is_absolute_path(""));
    #ifdef _WIN32
        assert(!is_absolute_path("path\\test"));
        assert(is_absolute_path("c:\\test"));
        assert(!is_absolute_path("c:test"));
        assert(is_absolute_path("c:\\"));
        assert(is_absolute_path("c:/"));
        assert(!is_absolute_path("c:"));
        assert(is_absolute_path("\\\\foo"));
        assert(is_absolute_path("\\\\?\\foo"));
        assert(is_absolute_path("\\\\?\\UNC\\foo"));
        assert(is_absolute_path("/foo"));
        assert(is_absolute_path("\\foo"));
    #else
        assert(!is_absolute_path("test/path"));
        assert(is_absolute_path("/test" ));
        assert(is_absolute_path("/" ));
    #endif
    }
    
    static uint32_t
    rand_uint32(void)
    {
        /* First decide how many bits we’ll actually use (1-32) */
        int bits = 1 + (rand() % 32);
    
        /* Generate a number with that many bits */
        uint32_t result = 0;
        while (bits > 0) {
            int bits_this_round = bits > 16 ? 16 : bits;
            result = (result << bits_this_round)
                   | (rand() & ((1U << bits_this_round) - 1));
            bits -= bits_this_round;
        }
    
        return result;
    }
    
    static uint64_t
    rand_uint64(void)
    {
        /* First decide how many bits we’ll actually use (1-64) */
        int bits = 1 + (rand() % 64);
    
        /* Generate a number with that many bits */
        uint64_t result = 0;
        while (bits > 0) {
            int bits_this_round = bits > 16 ? 16 : bits;
            result = (result << bits_this_round)
                   | (rand() & ((1U << bits_this_round) - 1));
            bits -= bits_this_round;
        }
    
        return result;
    }
    
    /* NOLINTBEGIN(google-readability-function-size) */
    static void
    test_number_parsers(void)
    {
        /* Check the claim that it always works on normal strings using SIZE_MAX and
         * that it always stops on the first NULL byte */
        {
            static const struct {
                const char* input;
                struct { int count; uint64_t val; } dec;
                struct { int count; uint64_t val; } hex;
            } tests[] = {
                {
                    .input = "",
                    .dec = { 0, UINT64_C(0) },
                    .hex = { 0, UINT64_C(0) }
                },
                {
                    .input = "\0""123",
                    .dec = { 0, UINT64_C(0) },
                    .hex = { 0, UINT64_C(0) }
                },
                {
                    .input = "/",
                    .dec = { 0, UINT64_C(0) },
                    .hex = { 0, UINT64_C(0) }
                },
                {
                    .input = ";",
                    .dec = { 0, UINT64_C(0) },
                    .hex = { 0, UINT64_C(0) }
                },
                {
                    .input = "x",
                    .dec = { 0, UINT64_C(0) },
                    .hex = { 0, UINT64_C(0) }
                },
                {
                    .input = "/1",
                    .dec = { 0, UINT64_C(0) },
                    .hex = { 0, UINT64_C(0) }
                },
                {
                    .input = ";1",
                    .dec = { 0, UINT64_C(0) },
                    .hex = { 0, UINT64_C(0) }
                },
                {
                    .input = "x1",
                    .dec = { 0, UINT64_C(0) },
                    .hex = { 0, UINT64_C(0) }
                },
                {
                    .input = "0",
                    .dec = { 1, UINT64_C(0) },
                    .hex = { 1, UINT64_C(0) }
                },
                {
                    .input = "1",
                    .dec = { 1, UINT64_C(1) },
                    .hex = { 1, UINT64_C(1) }
                },
                {
                    .input = "123",
                    .dec = { 3, UINT64_C(123)  },
                    .hex = { 3, UINT64_C(0x123)}
                },
                {
                    .input = "123x",
                    .dec = { 3, UINT64_C(123)  },
                    .hex = { 3, UINT64_C(0x123)}
                },
                {
                    .input = "123""\0""456",
                    .dec = { 3, UINT64_C(123)  },
                    .hex = { 3, UINT64_C(0x123)}
                },
                {
                    .input = "18446744073709551615",
                    .dec = { 20, UINT64_MAX                  },
                    .hex = { -1, UINT64_C(0x1844674407370955)}
                },
                {
                    .input = "18446744073709551616",
                    .dec = { -1, UINT64_C(1844674407370955161)},
                    .hex = { -1, UINT64_C(0x1844674407370955) }
                },
                {
                    .input = "99999999999999999999",
                    .dec = { -1, UINT64_C(9999999999999999999)},
                    .hex = { -1, UINT64_C(0x9999999999999999) }
                },
                {
                    .input = "184467440737095516150",
                    .dec = { -1, UINT64_MAX                  },
                    .hex = { -1, UINT64_C(0x1844674407370955)}
                },
                {
                    .input = "00000000000000000",
                    .dec = { 17, UINT64_C(0) },
                    .hex = { 17, UINT64_C(0) }
                },
                {
                    .input = "00000000000000001",
                    .dec = { 17, UINT64_C(1) },
                    .hex = { 17, UINT64_C(1) }
                },
                {
                    .input = "ffffffffffffffff",
                    .dec = { 0 , 0         },
                    .hex = { 16, UINT64_MAX}
                },
                {
                    .input = "ffffffffffffffff0",
                    .dec = { 0 , 0         },
                    .hex = { -1, UINT64_MAX}
                },
                {
                    .input = "10000000000000000",
                    .dec = { 17, UINT64_C(10000000000000000) },
                    .hex = { -1, UINT64_C(0x1000000000000000)}
                },
                {
                    .input = "fffffffffffffffff",
                    .dec = { 0 , 0         },
                    .hex = { -1, UINT64_MAX}
                },
    
            };
            for (size_t k = 0; k < ARRAY_SIZE(tests); k++) {
                const size_t len = strlen(tests[k].input);
                /* Try different lengths */
                const struct {const char* label; size_t len;} sizes[] = {
                    { .label = "buffer"  , .len = len     },
                    { .label = "string"  , .len = len + 1 },
                    { .label = "SIZE_MAX", .len = SIZE_MAX}
                };
                for (unsigned int s = 0; s < ARRAY_SIZE(sizes); s++) {
                    int count;
                    /* Decimal */
                    uint64_t dec = 0;
                    count =
                        parse_dec_to_uint64_t(tests[k].input, sizes[s].len, &dec);
                    assert_printf(count == tests[k].dec.count,
                                  "Dec %s #%zu \"%s\" (%zu), "
                                  "expected: %d, got: %d\n",
                                  sizes[s].label, k, tests[k].input, sizes[s].len,
                                  tests[k].dec.count, count);
                    assert_printf(dec == tests[k].dec.val,
                                  "Dec %s #%zu \"%s\", "
                                  "expected: %"PRIu64", got: %"PRIu64"\n",
                                  sizes[s].label, k, tests[k].input,
                                  tests[k].dec.val, dec);
                    /* Hexadecimal */
                    uint64_t hex = 0;
                    count =
                        parse_hex_to_uint64_t(tests[k].input, sizes[s].len, &hex);
                    assert_printf(count == tests[k].hex.count,
                                  "Hex %s #%zu \"%s\" (%zu), "
                                  "expected: %d, got: %d\n",
                                  sizes[s].label, k, tests[k].input, sizes[s].len,
                                  tests[k].hex.count, count);
                    assert_printf(hex == tests[k].hex.val,
                                  "Hex %s #%zu \"%s\", "
                                  "expected: %#"PRIx64", got: %#"PRIx64"\n",
                                  sizes[s].label, k, tests[k].input,
                                  tests[k].hex.val, hex);
                }
            }
        }
    
    #define PRIuint64_t PRIx64
    #define PRIuint32_t PRIx32
    #define test_parse_to(type, format, input, count, expected) do {     \
        type n = 0;                                                      \
        int r = parse_##format##_to_##type(input, ARRAY_SIZE(input), &n);\
        assert_printf(r == (count),                                      \
                      "Buffer: expected count: %d, "                     \
                      "got: %d (value: %#"PRI##type", string: %.*s)\n",  \
                      count, r, n, (int) ARRAY_SIZE(input), (input));    \
        assert_printf(n == (expected),                                   \
                      "Buffer: expected value: %#"PRI##type", got: "     \
                      "%#"PRI##type"\n", expected, n);                   \
    } while (0)
    
        /* Test syntax variants */
        const uint64_t values[] = {
            UINT64_C(0),
            UINT64_C(1),
            UINT64_C(10),
            UINT64_C(0xA),
            UINT64_C(0xF),
            UINT64_C(123),
            UINT32_MAX / 10,
            UINT32_MAX / 10 + 9,
            UINT32_MAX >> 4,
            UINT32_MAX >> 4 | 0xf,
            UINT32_MAX - 1,
            UINT32_MAX,
            UINT32_MAX + UINT64_C(1),
            UINT64_C(9999999999999999999),
            UINT64_MAX / 10,
            UINT64_MAX / 10 + 9,
            UINT64_MAX >> 4,
            UINT64_MAX >> 4 | 0xf,
            UINT64_MAX - 1,
            UINT64_MAX,
        };
        char buffer[30] = {0};
        for (size_t k = 0; k < ARRAY_SIZE(values); k++) {
            int count;
            /* Basic: decimal */
            count = snprintf(buffer, sizeof(buffer), "%"PRIu32, (uint32_t) values[k]);
            assert(count > 0);
            test_parse_to(uint32_t, dec, buffer, count, (uint32_t) values[k]);
            count = snprintf(buffer, sizeof(buffer), "%"PRIu64, values[k]);
            assert(count > 0);
            test_parse_to(uint64_t, dec, buffer, count, values[k]);
            /* Basic: hex lower case */
            count = snprintf(buffer, sizeof(buffer), "%"PRIx32, (uint32_t) values[k]);
            assert(count > 0);
            test_parse_to(uint32_t, hex, buffer, count, (uint32_t) values[k]);
            count = snprintf(buffer, sizeof(buffer), "%"PRIx64, values[k]);
            assert(count > 0);
            test_parse_to(uint64_t, hex, buffer, count, values[k]);
            /* Basic: hex upper case */
            count = snprintf(buffer, sizeof(buffer), "%"PRIX32, (uint32_t) values[k]);
            assert(count > 0);
            test_parse_to(uint32_t, hex, buffer, count, (uint32_t) values[k]);
            count = snprintf(buffer, sizeof(buffer), "%"PRIX64, values[k]);
            assert(count > 0);
            test_parse_to(uint64_t, hex, buffer, count, values[k]);
            /* Prefix with some zeroes */
            for (int z = 0; z < 10 ; z++) {
                /* Decimal */
                count = snprintf(buffer, sizeof(buffer), "%0*"PRIu32,
                                 z, (uint32_t) values[k]);
                assert(count > 0);
                test_parse_to(uint32_t, dec, buffer, count, (uint32_t) values[k]);
                count = snprintf(buffer, sizeof(buffer), "%0*"PRIu64,
                                 z, values[k]);
                assert(count > 0);
                test_parse_to(uint64_t, dec, buffer, count, values[k]);
                /* Hexadecimal */
                count = snprintf(buffer, sizeof(buffer), "%0*u%"PRIx32,
                                 z, 0, (uint32_t) values[k]);
                assert(count > 0);
                test_parse_to(uint32_t, hex, buffer, count, (uint32_t) values[k]);
                count = snprintf(buffer, sizeof(buffer), "%0*u%"PRIx64,
                                 z, 0, values[k]);
                assert(count > 0);
                test_parse_to(uint64_t, hex, buffer, count, values[k]);
                /* Append some garbage */
                for (int c = 0; c < 0x100 ; c++) {
                    if (c < '0' || c > '9') {
                        /* Decimal */
                        count = snprintf(buffer, sizeof(buffer), "%0*u%"PRIu32"%c",
                                         z, 0, (uint32_t) values[k], (char) c);
                        assert(count > 0);
                        test_parse_to(uint32_t, dec, buffer, count - 1, (uint32_t) values[k]);
                        count = snprintf(buffer, sizeof(buffer), "%0*u%"PRIu64"%c",
                                         z, 0, values[k], (char) c);
                        assert(count > 0);
                        test_parse_to(uint64_t, dec, buffer, count - 1, values[k]);
                    }
                    if (!is_xdigit((char) c)) {
                        /* Hexadecimal */
                        count = snprintf(buffer, sizeof(buffer), "%0*u%"PRIx32"%c",
                                         z, 0, (uint32_t) values[k], (char) c);
                        assert(count > 0);
                        test_parse_to(uint32_t, hex, buffer, count - 1, (uint32_t) values[k]);
                        count = snprintf(buffer, sizeof(buffer), "%0*u%"PRIx64"%c",
                                         z, 0, values[k], (char) c);
                        assert(count > 0);
                        test_parse_to(uint64_t, hex, buffer, count - 1, values[k]);
                    }
                }
            }
        }
    
        /* Random */
        for (unsigned int k = 0; k < 10000; k++) {
            const uint32_t x32 = rand_uint32();
            const uint64_t x64 = rand_uint64();
            int count;
            /* Hex: Lower case */
            count = snprintf(buffer, sizeof(buffer), "%"PRIx32, x32);
            assert(count > 0);
            test_parse_to(uint32_t, hex, buffer, count, x32);
            count = snprintf(buffer, sizeof(buffer), "%"PRIx64, x64);
            assert(count > 0);
            test_parse_to(uint64_t, hex, buffer, count, x64);
            /* Hex: Upper case (32 bits) */
            count = snprintf(buffer, sizeof(buffer), "%"PRIX32, x32);
            assert(count > 0);
            test_parse_to(uint32_t, hex, buffer, count, x32);
            /* Hex: some garbage after */
            buffer[count] = (char) (((unsigned int) rand()) % '0');
            test_parse_to(uint32_t, hex, buffer, count, x32);
            /* Hex: Upper case (64 bits) */
            count = snprintf(buffer, sizeof(buffer), "%"PRIX64, x64);
            assert(count > 0);
            test_parse_to(uint64_t, hex, buffer, count, x64);
            /* Hex: some garbage after */
            buffer[count] = (char) (((unsigned int) rand()) % '0');
            test_parse_to(uint64_t, hex, buffer, count, x64);
            /* Decimal (32 bits) */
            count = snprintf(buffer, sizeof(buffer), "%"PRIu32, x32);
            assert(count > 0);
            test_parse_to(uint32_t, dec, buffer, count, x32);
            /* Decimal: some garbage after */
            buffer[count] = (char) (((unsigned int) rand()) % '0');
            test_parse_to(uint32_t, dec, buffer, count, x32);
            /* Decimal (64 bits) */
            count = snprintf(buffer, sizeof(buffer), "%"PRIu64, x64);
            assert(count > 0);
            test_parse_to(uint64_t, dec, buffer, count, x64);
            buffer[count] = (char) (((unsigned int) rand()) % '0');
            test_parse_to(uint64_t, dec, buffer, count, x64);
        }
    }
    /* NOLINTEND(google-readability-function-size) */
    
    /* CLI positional arguments:
     * 1. Seed for the pseudo-random generator:
     *    - Leave it unset or set it to “-” to use current time.
     *    - Use an integer to set it explicitly.
     */
    int
    main(int argc, char *argv[])
    {
        test_init();
    
        /* Initialize pseudo-random generator with program arg or current time */
        unsigned int seed;
        if (argc >= 2 && !streq(argv[1], "-")) {
            seed = (unsigned int) atoi(argv[1]);
        } else {
            seed = (unsigned int) time(NULL);
        }
        fprintf(stderr, "Seed for the pseudo-random generator: %u\n", seed);
        srand(seed);
    
        test_string_functions();
        test_path_functions();
        test_number_parsers();
    
        return EXIT_SUCCESS;
    }