Branch :
/*
* Copyright © 2025 Pierre Le Marre <dev@wismill.eu>
* SPDX-License-Identifier: MIT
*/
#include "config.h"
#include "test.h"
#ifdef _WIN32
int
main(void)
{
test_init();
return SKIP_TEST;
}
#else
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include "xkbcommon/xkbcommon-keysyms.h"
#include "xkbcommon/xkbcommon.h"
#include "test.h"
#include "utils.h"
#include "src/keysym.h"
static void
test_unicode_keysyms_consistency(uint32_t start, uint32_t end)
{
static_assert(XKB_KEYSYM_NAME_MAX_SIZE > XKB_KEYSYM_UTF8_MAX_SIZE,
"Buffer too small");
char buffer[XKB_KEYSYM_NAME_MAX_SIZE] = {0};
char utf8[XKB_KEYSYM_UTF8_MAX_SIZE] = {0};
for (uint32_t cp = start; cp <= end; cp++) {
xkb_keysym_t unicode = XKB_KEYSYM_UNICODE_OFFSET + cp;
xkb_keysym_t canonical = xkb_utf32_to_keysym(cp);
int count = xkb_keysym_get_name(unicode, buffer, sizeof(buffer));
assert(count > 0);
if (cp == 0 || is_surrogate(cp)) {
/*
* Invalid code points
*/
/* No conversion from code point */
assert(canonical == XKB_KEY_NoSymbol);
/* No conversion to code point */
assert(xkb_keysym_to_utf32(unicode) == 0);
char *end_ptr = NULL;
if (cp == 0) {
/* Corresponding name hexadecimal format, unchanged */
assert(strtoull(buffer, &end_ptr, 16) == unicode);
} else {
/* Unicode notation */
assert(count == 5 && buffer[0] == 'U');
assert(strtoull(buffer + 1, &end_ptr, 16) == cp);
}
assert(*end_ptr == '\0');
/* Roundtrip hexadecimal name */
xkb_keysym_t ks = xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS);
assert(ks == unicode);
/* Check Unicode format */
snprintf(buffer, sizeof(buffer), "U%"PRIX32, cp);
ks = xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS);
assert((cp == 0 && ks == XKB_KEY_NoSymbol) ^
(is_surrogate(cp) && ks == unicode));
/* Cannot convert to UTF-8 */
count = xkb_keysym_to_utf8(unicode, buffer, sizeof(buffer));
assert(count == 0);
} else {
/*
* Valid code points
*/
/* Canonical keysym may be different but is set */
assert_printf((canonical == unicode) ^
((0x20 <= cp && cp <= 0x100 && cp != 0x7f && canonical == cp) ||
(canonical != unicode &&
canonical != XKB_KEY_NoSymbol &&
(canonical != cp || canonical == XKB_KEY_EuroSign))),
"Code point: U+%04"PRIX32", Unicode: %#"PRIx32", "
"canonical: %#"PRIx32"\n",
cp, unicode, canonical);
/* Conversion to code point has the same expected result */
assert(xkb_keysym_to_utf32(unicode) == cp);
assert(xkb_keysym_to_utf32(canonical) == cp); /* roundtrip */
/* Check name roundtrip */
xkb_keysym_t ks = xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS);
assert((unicode != canonical && ks == canonical) ^ (ks == unicode));
/* Can use Unicode format */
if (buffer[0] == 'U' && count > 4 && is_digit(buffer[1])) {
/* Name already a Unicode notation: skip.
* Note that the heuristic may fail to detect a Unicode notation,
* but it is only meant to make the overall test faster. */
} else {
snprintf(buffer, sizeof(buffer), "U%"PRIX32, cp);
ks = xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS);
assert((unicode != canonical && ks == canonical) ^ (ks == unicode));
}
/* Roundtrip: numeric hexadecimal format for Unicode keysym */
assert(snprintf(buffer, sizeof(buffer), "%#"PRIx32, unicode) == 9);
ks = xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS);
assert(ks == unicode);
/* Can convert to UTF-8 (Unicode keysym) */
count = xkb_keysym_to_utf8(unicode, buffer, sizeof(buffer));
assert(count > 0);
if (canonical != unicode) {
/* Canonical keysym convert to same UTF-8 */
const int count2 = xkb_keysym_to_utf8(canonical, utf8, sizeof(utf8));
assert(count2 == count);
assert(strcmp(buffer, utf8) == 0);
/* Roundtrip: numeric hexadecimal format for canonical keysym */
count = xkb_keysym_get_name(canonical, buffer, sizeof(buffer));
assert(count > 0);
assert(xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS) == canonical);
assert(snprintf(buffer, sizeof(buffer), "%#"PRIx32, canonical) > 2);
assert(xkb_keysym_from_name(buffer, XKB_KEYSYM_NO_FLAGS) == canonical);
}
}
}
}
int
main(int argc, char *argv[])
{
test_init();
unsigned long int NUM_PROCESSES = 0;
if (argc > 1) {
NUM_PROCESSES = strtoul(argv[1], NULL, 10);
}
if (NUM_PROCESSES == 0 || NUM_PROCESSES > 32)
NUM_PROCESSES = 4;
const uint32_t max_codepoint = 0x10ffff;
const uint32_t chunk = max_codepoint / NUM_PROCESSES;
pid_t pids[NUM_PROCESSES];
for (unsigned long int i = 0; i < NUM_PROCESSES; i++) {
pid_t pid = fork();
if (pid == 0) {
/* Child */
const uint32_t start = i * chunk;
const uint32_t end = (i == NUM_PROCESSES - 1)
? max_codepoint
: start + chunk;
test_unicode_keysyms_consistency(start, end);
exit(EXIT_SUCCESS);
} else if (pid > 0) {
/* Parent */
pids[i] = pid;
} else {
perror("fork");
return TEST_SETUP_FAILURE;
}
}
/* Wait for children */
int exit_code = EXIT_SUCCESS;
for (unsigned long int i = 0; i < NUM_PROCESSES; i++) {
int status;
if (waitpid(pids[i], &status, 0) == -1) {
perror("waitpid");
exit_code = EXIT_FAILURE;
} else if (WIFEXITED(status)) {
int code = WEXITSTATUS(status);
if (code != EXIT_SUCCESS) {
fprintf(stderr,
"Child PID %d exited with code %d\n",
pids[i], code);
exit_code = EXIT_FAILURE;
}
} else if (WIFSIGNALED(status)) {
fprintf(stderr,
"Child PID %d terminated by signal %d\n",
pids[i], WTERMSIG(status));
exit_code = EXIT_FAILURE;
}
}
return exit_code;
}
#endif