2013-10-27 12:37:27 -06:00
|
|
|
|
/*
|
|
|
|
|
* Copyright © 2014 Ran Benita <ran234@gmail.com>
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
|
|
|
* to deal in the Software without restriction, including without limitation
|
|
|
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
|
|
|
*
|
|
|
|
|
* The above copyright notice and this permission notice (including the next
|
|
|
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
|
|
|
* Software.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
|
* DEALINGS IN THE SOFTWARE.
|
|
|
|
|
*/
|
|
|
|
|
|
2019-12-27 04:03:20 -07:00
|
|
|
|
#include "config.h"
|
2023-11-07 04:58:41 -07:00
|
|
|
|
#include <time.h>
|
2019-12-27 04:03:20 -07:00
|
|
|
|
|
2013-10-27 12:37:27 -06:00
|
|
|
|
#include "xkbcommon/xkbcommon-compose.h"
|
|
|
|
|
|
|
|
|
|
#include "test.h"
|
2023-11-07 04:58:41 -07:00
|
|
|
|
#include "src/utf8.h"
|
|
|
|
|
#include "src/compose/parser.h"
|
|
|
|
|
#include "src/compose/dump.h"
|
2013-10-27 12:37:27 -06:00
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
compose_status_string(enum xkb_compose_status status)
|
|
|
|
|
{
|
|
|
|
|
switch (status) {
|
|
|
|
|
case XKB_COMPOSE_NOTHING:
|
|
|
|
|
return "nothing";
|
|
|
|
|
case XKB_COMPOSE_COMPOSING:
|
|
|
|
|
return "composing";
|
|
|
|
|
case XKB_COMPOSE_COMPOSED:
|
|
|
|
|
return "composed";
|
|
|
|
|
case XKB_COMPOSE_CANCELLED:
|
|
|
|
|
return "cancelled";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "<invalid-status>";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
feed_result_string(enum xkb_compose_feed_result result)
|
|
|
|
|
{
|
|
|
|
|
switch (result) {
|
|
|
|
|
case XKB_COMPOSE_FEED_IGNORED:
|
|
|
|
|
return "ignored";
|
|
|
|
|
case XKB_COMPOSE_FEED_ACCEPTED:
|
|
|
|
|
return "accepted";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "<invalid-result>";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Feed a sequence of keysyms to a fresh compose state and test the outcome.
|
|
|
|
|
*
|
|
|
|
|
* The varargs consists of lines in the following format:
|
|
|
|
|
* <input keysym> <expected feed result> <expected status> <expected string> <expected keysym>
|
|
|
|
|
* Terminated by a line consisting only of XKB_KEY_NoSymbol.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
test_compose_seq_va(struct xkb_compose_table *table, va_list ap)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
struct xkb_compose_state *state;
|
|
|
|
|
char buffer[64];
|
|
|
|
|
|
|
|
|
|
state = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS);
|
|
|
|
|
assert(state);
|
|
|
|
|
|
|
|
|
|
for (int i = 1; ; i++) {
|
|
|
|
|
xkb_keysym_t input_keysym;
|
|
|
|
|
enum xkb_compose_feed_result result, expected_result;
|
|
|
|
|
enum xkb_compose_status status, expected_status;
|
|
|
|
|
const char *expected_string;
|
|
|
|
|
xkb_keysym_t keysym, expected_keysym;
|
|
|
|
|
|
|
|
|
|
input_keysym = va_arg(ap, xkb_keysym_t);
|
|
|
|
|
if (input_keysym == XKB_KEY_NoSymbol)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
expected_result = va_arg(ap, enum xkb_compose_feed_result);
|
|
|
|
|
expected_status = va_arg(ap, enum xkb_compose_status);
|
|
|
|
|
expected_string = va_arg(ap, const char *);
|
|
|
|
|
expected_keysym = va_arg(ap, xkb_keysym_t);
|
|
|
|
|
|
|
|
|
|
result = xkb_compose_state_feed(state, input_keysym);
|
|
|
|
|
|
|
|
|
|
if (result != expected_result) {
|
|
|
|
|
fprintf(stderr, "after feeding %d keysyms:\n", i);
|
|
|
|
|
fprintf(stderr, "expected feed result: %s\n",
|
|
|
|
|
feed_result_string(expected_result));
|
|
|
|
|
fprintf(stderr, "got feed result: %s\n",
|
|
|
|
|
feed_result_string(result));
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = xkb_compose_state_get_status(state);
|
|
|
|
|
if (status != expected_status) {
|
|
|
|
|
fprintf(stderr, "after feeding %d keysyms:\n", i);
|
|
|
|
|
fprintf(stderr, "expected status: %s\n",
|
|
|
|
|
compose_status_string(expected_status));
|
|
|
|
|
fprintf(stderr, "got status: %s\n",
|
|
|
|
|
compose_status_string(status));
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = xkb_compose_state_get_utf8(state, buffer, sizeof(buffer));
|
|
|
|
|
if (ret < 0 || (size_t) ret >= sizeof(buffer)) {
|
|
|
|
|
fprintf(stderr, "after feeding %d keysyms:\n", i);
|
|
|
|
|
fprintf(stderr, "expected string: %s\n", expected_string);
|
|
|
|
|
fprintf(stderr, "got error: %d\n", ret);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
if (!streq(buffer, expected_string)) {
|
|
|
|
|
fprintf(stderr, "after feeding %d keysyms:\n", i);
|
|
|
|
|
fprintf(stderr, "expected string: %s\n", strempty(expected_string));
|
|
|
|
|
fprintf(stderr, "got string: %s\n", buffer);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keysym = xkb_compose_state_get_one_sym(state);
|
|
|
|
|
if (keysym != expected_keysym) {
|
|
|
|
|
fprintf(stderr, "after feeding %d keysyms:\n", i);
|
|
|
|
|
xkb_keysym_get_name(expected_keysym, buffer, sizeof(buffer));
|
|
|
|
|
fprintf(stderr, "expected keysym: %s\n", buffer);
|
|
|
|
|
xkb_keysym_get_name(keysym, buffer, sizeof(buffer));
|
|
|
|
|
fprintf(stderr, "got keysym (%#x): %s\n", keysym, buffer);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xkb_compose_state_unref(state);
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
xkb_compose_state_unref(state);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
test_compose_seq(struct xkb_compose_table *table, ...)
|
|
|
|
|
{
|
|
|
|
|
va_list ap;
|
|
|
|
|
bool ok;
|
|
|
|
|
va_start(ap, table);
|
|
|
|
|
ok = test_compose_seq_va(table, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
test_compose_seq_buffer(struct xkb_context *ctx, const char *buffer, ...)
|
|
|
|
|
{
|
|
|
|
|
va_list ap;
|
|
|
|
|
bool ok;
|
|
|
|
|
struct xkb_compose_table *table;
|
|
|
|
|
table = xkb_compose_table_new_from_buffer(ctx, buffer, strlen(buffer), "",
|
|
|
|
|
XKB_COMPOSE_FORMAT_TEXT_V1,
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(table);
|
|
|
|
|
va_start(ap, buffer);
|
|
|
|
|
ok = test_compose_seq_va(table, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
xkb_compose_table_unref(table);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 00:20:29 -06:00
|
|
|
|
static void
|
|
|
|
|
test_compose_utf8_bom(struct xkb_context *ctx)
|
|
|
|
|
{
|
2023-10-30 07:50:00 -06:00
|
|
|
|
const char buffer[] = "\xef\xbb\xbf<A> : X";
|
2023-10-29 00:20:29 -06:00
|
|
|
|
assert(test_compose_seq_buffer(ctx, buffer,
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "X", XKB_KEY_X,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 07:50:00 -06:00
|
|
|
|
static void
|
|
|
|
|
test_invalid_encodings(struct xkb_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
struct xkb_compose_table *table;
|
|
|
|
|
|
|
|
|
|
/* ISO 8859-1 (latin1) */
|
|
|
|
|
const char iso_8859_1[] = "<A> : \"\xe1\" acute";
|
|
|
|
|
assert(!test_compose_seq_buffer(ctx, iso_8859_1,
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "\xc3\xa1", XKB_KEY_acute,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
/* UTF-16LE */
|
|
|
|
|
const char utf_16_le[] =
|
|
|
|
|
"<\0A\0>\0 \0:\0 \0X\0\n\0"
|
|
|
|
|
"<\0B\0>\0 \0:\0 \0Y\0";
|
|
|
|
|
table = xkb_compose_table_new_from_buffer(ctx,
|
|
|
|
|
utf_16_le, sizeof(utf_16_le), "",
|
|
|
|
|
XKB_COMPOSE_FORMAT_TEXT_V1,
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(!table);
|
|
|
|
|
|
|
|
|
|
/* UTF-16BE */
|
|
|
|
|
const char utf_16_be[] =
|
|
|
|
|
"\0<\0A\0>\0 \0:\0 \0X\0\n"
|
|
|
|
|
"\0<\0B\0>\0 \0:\0 \0Y";
|
|
|
|
|
table = xkb_compose_table_new_from_buffer(ctx,
|
|
|
|
|
utf_16_be, sizeof(utf_16_be), "",
|
|
|
|
|
XKB_COMPOSE_FORMAT_TEXT_V1,
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(!table);
|
|
|
|
|
|
|
|
|
|
/* UTF-16BE with BOM */
|
|
|
|
|
const char utf_16_be_bom[] =
|
|
|
|
|
"\xfe\xff"
|
|
|
|
|
"\0<\0A\0>\0 \0:\0 \0X\0\n"
|
|
|
|
|
"\0<\0B\0>\0 \0:\0 \0Y";
|
|
|
|
|
table = xkb_compose_table_new_from_buffer(ctx,
|
|
|
|
|
utf_16_be_bom, sizeof(utf_16_be_bom), "",
|
|
|
|
|
XKB_COMPOSE_FORMAT_TEXT_V1,
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(!table);
|
|
|
|
|
|
|
|
|
|
/* UTF-32LE */
|
|
|
|
|
const char utf_32_le[] =
|
|
|
|
|
"<\0\0\0A\0\0\0>\0\0\0 \0\0\0:\0\0\0 \0\0\0X\0\0\0\n\0\0\0"
|
|
|
|
|
"<\0\0\0B\0\0\0>\0\0\0 \0\0\0:\0\0\0 \0\0\0Y\0\0\0";
|
|
|
|
|
table = xkb_compose_table_new_from_buffer(ctx,
|
|
|
|
|
utf_32_le, sizeof(utf_32_le), "",
|
|
|
|
|
XKB_COMPOSE_FORMAT_TEXT_V1,
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(!table);
|
|
|
|
|
|
|
|
|
|
/* UTF-32LE with BOM */
|
|
|
|
|
const char utf_32_le_bom[] =
|
|
|
|
|
"\xff\xfe\0\0"
|
|
|
|
|
"<\0\0\0A\0\0\0>\0\0\0 \0\0\0:\0\0\0 \0\0\0X\0\0\0\n\0\0\0"
|
|
|
|
|
"<\0\0\0B\0\0\0>\0\0\0 \0\0\0:\0\0\0 \0\0\0Y\0\0\0";
|
|
|
|
|
table = xkb_compose_table_new_from_buffer(ctx,
|
|
|
|
|
utf_32_le_bom, sizeof(utf_32_le_bom), "",
|
|
|
|
|
XKB_COMPOSE_FORMAT_TEXT_V1,
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(!table);
|
|
|
|
|
|
|
|
|
|
/* UTF-32BE */
|
|
|
|
|
const char utf_32_be[] =
|
|
|
|
|
"\0\0\0<\0\0\0A\0\0\0>\0\0\0 \0\0\0:\0\0\0 \0\0\0X\0\0\0\n\0\0\0"
|
|
|
|
|
"<\0\0\0B\0\0\0>\0\0\0 \0\0\0:\0\0\0 \0\0\0Y";
|
|
|
|
|
table = xkb_compose_table_new_from_buffer(ctx,
|
|
|
|
|
utf_32_be, sizeof(utf_32_be), "",
|
|
|
|
|
XKB_COMPOSE_FORMAT_TEXT_V1,
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(!table);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 00:20:29 -06:00
|
|
|
|
|
2013-10-27 12:37:27 -06:00
|
|
|
|
static void
|
|
|
|
|
test_seqs(struct xkb_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
struct xkb_compose_table *table;
|
|
|
|
|
char *path;
|
|
|
|
|
FILE *file;
|
|
|
|
|
|
2021-05-22 10:55:04 -06:00
|
|
|
|
path = test_get_path("locale/en_US.UTF-8/Compose");
|
2019-12-28 04:49:40 -07:00
|
|
|
|
file = fopen(path, "rb");
|
2013-10-27 12:37:27 -06:00
|
|
|
|
assert(file);
|
|
|
|
|
free(path);
|
|
|
|
|
|
|
|
|
|
table = xkb_compose_table_new_from_file(ctx, file, "",
|
|
|
|
|
XKB_COMPOSE_FORMAT_TEXT_V1,
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(table);
|
|
|
|
|
fclose(file);
|
|
|
|
|
|
|
|
|
|
assert(test_compose_seq(table,
|
|
|
|
|
XKB_KEY_dead_tilde, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_space, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "~", XKB_KEY_asciitilde,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
assert(test_compose_seq(table,
|
|
|
|
|
XKB_KEY_dead_tilde, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_space, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "~", XKB_KEY_asciitilde,
|
|
|
|
|
XKB_KEY_dead_tilde, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_space, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "~", XKB_KEY_asciitilde,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
assert(test_compose_seq(table,
|
|
|
|
|
XKB_KEY_dead_tilde, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_dead_tilde, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "~", XKB_KEY_asciitilde,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
assert(test_compose_seq(table,
|
|
|
|
|
XKB_KEY_dead_acute, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_space, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "'", XKB_KEY_apostrophe,
|
|
|
|
|
XKB_KEY_Caps_Lock, XKB_COMPOSE_FEED_IGNORED, XKB_COMPOSE_COMPOSED, "'", XKB_KEY_apostrophe,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
assert(test_compose_seq(table,
|
|
|
|
|
XKB_KEY_dead_acute, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_dead_acute, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "´", XKB_KEY_acute,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
assert(test_compose_seq(table,
|
|
|
|
|
XKB_KEY_Multi_key, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_Shift_L, XKB_COMPOSE_FEED_IGNORED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_Caps_Lock, XKB_COMPOSE_FEED_IGNORED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_Control_L, XKB_COMPOSE_FEED_IGNORED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_T, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "@", XKB_KEY_at,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
assert(test_compose_seq(table,
|
|
|
|
|
XKB_KEY_7, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_a, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_b, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
assert(test_compose_seq(table,
|
|
|
|
|
XKB_KEY_Multi_key, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_apostrophe, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_7, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_CANCELLED, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_7, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_Caps_Lock, XKB_COMPOSE_FEED_IGNORED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
xkb_compose_table_unref(table);
|
|
|
|
|
|
|
|
|
|
/* Make sure one-keysym sequences work. */
|
|
|
|
|
assert(test_compose_seq_buffer(ctx,
|
|
|
|
|
"<A> : \"foo\" X \n"
|
|
|
|
|
"<B> <A> : \"baz\" Y \n",
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_X,
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_X,
|
|
|
|
|
XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "baz", XKB_KEY_Y,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
/* No sequences at all. */
|
|
|
|
|
assert(test_compose_seq_buffer(ctx,
|
|
|
|
|
"",
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_Multi_key, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_dead_acute, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
/* Only keysym - string derived from keysym. */
|
|
|
|
|
assert(test_compose_seq_buffer(ctx,
|
|
|
|
|
"<A> <B> : X \n"
|
|
|
|
|
"<B> <A> : dollar \n"
|
|
|
|
|
"<C> : dead_acute \n",
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "X", XKB_KEY_X,
|
|
|
|
|
XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "$", XKB_KEY_dollar,
|
|
|
|
|
XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "", XKB_KEY_dead_acute,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
/* Make sure a cancelling keysym doesn't start a new sequence. */
|
|
|
|
|
assert(test_compose_seq_buffer(ctx,
|
|
|
|
|
"<A> <B> : X \n"
|
|
|
|
|
"<C> <D> : Y \n",
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_CANCELLED, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_D, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_CANCELLED, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_D, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "Y", XKB_KEY_Y,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
test_conflicting(struct xkb_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
// new is prefix of old
|
|
|
|
|
assert(test_compose_seq_buffer(ctx,
|
|
|
|
|
"<A> <B> <C> : \"foo\" A \n"
|
|
|
|
|
"<A> <B> : \"bar\" B \n",
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_A,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
// old is a prefix of new
|
|
|
|
|
assert(test_compose_seq_buffer(ctx,
|
|
|
|
|
"<A> <B> : \"bar\" B \n"
|
|
|
|
|
"<A> <B> <C> : \"foo\" A \n",
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_A,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
// new duplicate of old
|
|
|
|
|
assert(test_compose_seq_buffer(ctx,
|
|
|
|
|
"<A> <B> : \"bar\" B \n"
|
|
|
|
|
"<A> <B> : \"bar\" B \n",
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "bar", XKB_KEY_B,
|
|
|
|
|
XKB_KEY_C, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_NOTHING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
// new same length as old #1
|
|
|
|
|
assert(test_compose_seq_buffer(ctx,
|
|
|
|
|
"<A> <B> : \"foo\" A \n"
|
|
|
|
|
"<A> <B> : \"bar\" B \n",
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "bar", XKB_KEY_B,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
// new same length as old #2
|
|
|
|
|
assert(test_compose_seq_buffer(ctx,
|
|
|
|
|
"<A> <B> : \"foo\" A \n"
|
|
|
|
|
"<A> <B> : \"foo\" B \n",
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_B,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
// new same length as old #3
|
|
|
|
|
assert(test_compose_seq_buffer(ctx,
|
|
|
|
|
"<A> <B> : \"foo\" A \n"
|
|
|
|
|
"<A> <B> : \"bar\" A \n",
|
|
|
|
|
XKB_KEY_A, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_B, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "bar", XKB_KEY_A,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
test_state(struct xkb_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
struct xkb_compose_table *table;
|
|
|
|
|
struct xkb_compose_state *state;
|
|
|
|
|
char *path;
|
|
|
|
|
FILE *file;
|
|
|
|
|
|
2021-05-22 10:55:04 -06:00
|
|
|
|
path = test_get_path("locale/en_US.UTF-8/Compose");
|
2019-12-28 04:49:40 -07:00
|
|
|
|
file = fopen(path, "rb");
|
2013-10-27 12:37:27 -06:00
|
|
|
|
assert(file);
|
|
|
|
|
free(path);
|
|
|
|
|
|
|
|
|
|
table = xkb_compose_table_new_from_file(ctx, file, "",
|
|
|
|
|
XKB_COMPOSE_FORMAT_TEXT_V1,
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(table);
|
|
|
|
|
fclose(file);
|
|
|
|
|
|
|
|
|
|
state = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS);
|
|
|
|
|
assert(state);
|
|
|
|
|
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
|
|
|
|
|
xkb_compose_state_reset(state);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
|
|
|
|
|
xkb_compose_state_feed(state, XKB_KEY_NoSymbol);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
|
|
|
|
|
xkb_compose_state_feed(state, XKB_KEY_Multi_key);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
|
|
|
|
|
xkb_compose_state_reset(state);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
|
|
|
|
|
xkb_compose_state_feed(state, XKB_KEY_Multi_key);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
|
|
|
|
|
xkb_compose_state_feed(state, XKB_KEY_Multi_key);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_CANCELLED);
|
|
|
|
|
xkb_compose_state_feed(state, XKB_KEY_Multi_key);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
|
|
|
|
|
xkb_compose_state_feed(state, XKB_KEY_Multi_key);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_CANCELLED);
|
|
|
|
|
xkb_compose_state_reset(state);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
|
|
|
|
|
xkb_compose_state_feed(state, XKB_KEY_dead_acute);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
|
|
|
|
|
xkb_compose_state_feed(state, XKB_KEY_A);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSED);
|
|
|
|
|
xkb_compose_state_reset(state);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
|
|
|
|
|
xkb_compose_state_feed(state, XKB_KEY_dead_acute);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
|
|
|
|
|
xkb_compose_state_feed(state, XKB_KEY_A);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSED);
|
|
|
|
|
xkb_compose_state_reset(state);
|
|
|
|
|
xkb_compose_state_feed(state, XKB_KEY_NoSymbol);
|
|
|
|
|
assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
|
|
|
|
|
|
|
|
|
|
xkb_compose_state_unref(state);
|
|
|
|
|
xkb_compose_table_unref(table);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
test_XCOMPOSEFILE(struct xkb_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
struct xkb_compose_table *table;
|
|
|
|
|
char *path;
|
|
|
|
|
|
2021-05-22 10:55:04 -06:00
|
|
|
|
path = test_get_path("locale/en_US.UTF-8/Compose");
|
2013-10-27 12:37:27 -06:00
|
|
|
|
setenv("XCOMPOSEFILE", path, 1);
|
|
|
|
|
free(path);
|
|
|
|
|
|
|
|
|
|
table = xkb_compose_table_new_from_locale(ctx, "blabla",
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(table);
|
|
|
|
|
|
2021-05-22 11:00:24 -06:00
|
|
|
|
unsetenv("XCOMPOSEFILE");
|
|
|
|
|
|
2013-10-27 12:37:27 -06:00
|
|
|
|
assert(test_compose_seq(table,
|
|
|
|
|
XKB_KEY_dead_tilde, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_space, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "~", XKB_KEY_asciitilde,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
xkb_compose_table_unref(table);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-22 11:07:06 -06:00
|
|
|
|
static void
|
|
|
|
|
test_from_locale(struct xkb_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
struct xkb_compose_table *table;
|
|
|
|
|
char *path;
|
|
|
|
|
|
|
|
|
|
path = test_get_path("locale");
|
|
|
|
|
setenv("XLOCALEDIR", path, 1);
|
|
|
|
|
free(path);
|
|
|
|
|
|
|
|
|
|
/* Direct directory name match. */
|
|
|
|
|
table = xkb_compose_table_new_from_locale(ctx, "en_US.UTF-8",
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(table);
|
|
|
|
|
xkb_compose_table_unref(table);
|
|
|
|
|
|
|
|
|
|
/* Direct locale name match. */
|
|
|
|
|
table = xkb_compose_table_new_from_locale(ctx, "C.UTF-8",
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(table);
|
|
|
|
|
xkb_compose_table_unref(table);
|
|
|
|
|
|
|
|
|
|
/* Alias. */
|
|
|
|
|
table = xkb_compose_table_new_from_locale(ctx, "univ.utf8",
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(table);
|
|
|
|
|
xkb_compose_table_unref(table);
|
|
|
|
|
|
|
|
|
|
/* Special case - C. */
|
|
|
|
|
table = xkb_compose_table_new_from_locale(ctx, "C",
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(table);
|
|
|
|
|
xkb_compose_table_unref(table);
|
|
|
|
|
|
|
|
|
|
/* Bogus - not found. */
|
|
|
|
|
table = xkb_compose_table_new_from_locale(ctx, "blabla",
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(!table);
|
|
|
|
|
|
|
|
|
|
unsetenv("XLOCALEDIR");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-10-14 01:53:38 -06:00
|
|
|
|
static void
|
|
|
|
|
test_modifier_syntax(struct xkb_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
const char *table_string;
|
|
|
|
|
|
|
|
|
|
/* We don't do anything with the modifiers, but make sure we can parse
|
|
|
|
|
* them. */
|
|
|
|
|
|
|
|
|
|
assert(test_compose_seq_buffer(ctx,
|
|
|
|
|
"None <A> : X \n"
|
2015-03-24 08:40:29 -06:00
|
|
|
|
"Shift <B> : Y \n"
|
|
|
|
|
"Ctrl <C> : Y \n"
|
|
|
|
|
"Alt <D> : Y \n"
|
|
|
|
|
"Caps <E> : Y \n"
|
|
|
|
|
"Lock <F> : Y \n"
|
|
|
|
|
"Shift Ctrl <G> : Y \n"
|
|
|
|
|
"~Shift <H> : Y \n"
|
|
|
|
|
"~Shift Ctrl <I> : Y \n"
|
|
|
|
|
"Shift ~Ctrl <J> : Y \n"
|
|
|
|
|
"Shift ~Ctrl ~Alt <K> : Y \n"
|
2014-10-14 01:53:38 -06:00
|
|
|
|
"! Shift <B> : Y \n"
|
|
|
|
|
"! Ctrl <C> : Y \n"
|
|
|
|
|
"! Alt <D> : Y \n"
|
|
|
|
|
"! Caps <E> : Y \n"
|
|
|
|
|
"! Lock <F> : Y \n"
|
|
|
|
|
"! Shift Ctrl <G> : Y \n"
|
|
|
|
|
"! ~Shift <H> : Y \n"
|
|
|
|
|
"! ~Shift Ctrl <I> : Y \n"
|
|
|
|
|
"! Shift ~Ctrl <J> : Y \n"
|
|
|
|
|
"! Shift ~Ctrl ~Alt <K> : Y \n"
|
|
|
|
|
"<L> ! Shift <M> : Y \n"
|
|
|
|
|
"None <N> ! Shift <O> : Y \n"
|
|
|
|
|
"None <P> ! Shift <Q> : Y \n",
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "<START bad input string>\n");
|
|
|
|
|
table_string =
|
|
|
|
|
"! None <A> : X \n"
|
|
|
|
|
"! Foo <B> : X \n"
|
|
|
|
|
"None ! Shift <C> : X \n"
|
2015-03-24 08:40:29 -06:00
|
|
|
|
"! ! <D> : X \n"
|
2014-10-14 01:53:38 -06:00
|
|
|
|
"! ~ <E> : X \n"
|
|
|
|
|
"! ! <F> : X \n"
|
|
|
|
|
"! Ctrl ! Ctrl <G> : X \n"
|
|
|
|
|
"<H> ! : X \n"
|
|
|
|
|
"<I> None : X \n"
|
|
|
|
|
"None None <J> : X \n"
|
|
|
|
|
"<K> : !Shift X \n";
|
|
|
|
|
assert(!xkb_compose_table_new_from_buffer(ctx, table_string,
|
|
|
|
|
strlen(table_string), "C",
|
|
|
|
|
XKB_COMPOSE_FORMAT_TEXT_V1,
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS));
|
|
|
|
|
fprintf(stderr, "<END bad input string>\n");
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-14 02:47:25 -06:00
|
|
|
|
static void
|
|
|
|
|
test_include(struct xkb_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
char *path, *table_string;
|
|
|
|
|
|
2021-05-22 10:55:04 -06:00
|
|
|
|
path = test_get_path("locale/en_US.UTF-8/Compose");
|
2014-10-14 02:47:25 -06:00
|
|
|
|
assert(path);
|
|
|
|
|
|
|
|
|
|
/* We don't have a mechanism to change the include paths like we
|
|
|
|
|
* have for keymaps. So we must include the full path. */
|
2020-07-09 22:57:57 -06:00
|
|
|
|
table_string = asprintf_safe("<dead_tilde> <space> : \"foo\" X\n"
|
|
|
|
|
"include \"%s\"\n"
|
|
|
|
|
"<dead_tilde> <dead_tilde> : \"bar\" Y\n", path);
|
|
|
|
|
assert(table_string);
|
2014-10-14 02:47:25 -06:00
|
|
|
|
|
|
|
|
|
assert(test_compose_seq_buffer(ctx, table_string,
|
|
|
|
|
/* No conflict. */
|
|
|
|
|
XKB_KEY_dead_acute, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_dead_acute, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "´", XKB_KEY_acute,
|
|
|
|
|
|
|
|
|
|
/* Comes before - doesn't override. */
|
|
|
|
|
XKB_KEY_dead_tilde, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_space, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "~", XKB_KEY_asciitilde,
|
|
|
|
|
|
|
|
|
|
/* Comes after - does override. */
|
|
|
|
|
XKB_KEY_dead_tilde, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_dead_tilde, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "bar", XKB_KEY_Y,
|
|
|
|
|
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
|
|
|
|
|
free(path);
|
|
|
|
|
free(table_string);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-14 02:11:32 -06:00
|
|
|
|
static void
|
|
|
|
|
test_override(struct xkb_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
const char *table_string = "<dead_circumflex> <dead_circumflex> : \"foo\" X\n"
|
|
|
|
|
"<dead_circumflex> <e> : \"bar\" Y\n"
|
|
|
|
|
"<dead_circumflex> <dead_circumflex> <e> : \"baz\" Z\n";
|
|
|
|
|
|
|
|
|
|
assert(test_compose_seq_buffer(ctx, table_string,
|
|
|
|
|
/* Comes after - does override. */
|
|
|
|
|
XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_e, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "baz", XKB_KEY_Z,
|
|
|
|
|
|
|
|
|
|
/* Override does not affect sibling nodes */
|
|
|
|
|
XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_e, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "bar", XKB_KEY_Y,
|
|
|
|
|
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-25 03:41:48 -06:00
|
|
|
|
static bool
|
|
|
|
|
test_eq_entry_va(struct xkb_compose_table_entry *entry, xkb_keysym_t keysym_ref, const char *utf8_ref, va_list ap)
|
|
|
|
|
{
|
|
|
|
|
assert (entry != NULL);
|
|
|
|
|
|
|
|
|
|
assert (xkb_compose_table_entry_keysym(entry) == keysym_ref);
|
|
|
|
|
|
|
|
|
|
const char *utf8 = xkb_compose_table_entry_utf8(entry);
|
|
|
|
|
assert (utf8 && utf8_ref && strcmp(utf8, utf8_ref) == 0);
|
|
|
|
|
|
|
|
|
|
size_t nsyms;
|
|
|
|
|
const xkb_keysym_t *sequence = xkb_compose_table_entry_sequence(entry, &nsyms);
|
|
|
|
|
|
|
|
|
|
xkb_keysym_t keysym;
|
|
|
|
|
for (unsigned k = 0; ; k++) {
|
|
|
|
|
keysym = va_arg(ap, xkb_keysym_t);
|
|
|
|
|
if (keysym == XKB_KEY_NoSymbol) {
|
|
|
|
|
return (k == nsyms - 1);
|
|
|
|
|
}
|
|
|
|
|
assert (k < nsyms);
|
|
|
|
|
assert (keysym == sequence[k]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
test_eq_entry(struct xkb_compose_table_entry *entry, xkb_keysym_t keysym, const char *utf8, ...)
|
|
|
|
|
{
|
|
|
|
|
va_list ap;
|
|
|
|
|
bool ok;
|
|
|
|
|
va_start(ap, utf8);
|
|
|
|
|
ok = test_eq_entry_va(entry, keysym, utf8, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
test_traverse(struct xkb_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
struct xkb_compose_table *table;
|
|
|
|
|
|
|
|
|
|
const char *buffer = "<dead_circumflex> <dead_circumflex> : \"foo\" X\n"
|
|
|
|
|
"<Ahook> <x> : \"foobar\"\n"
|
|
|
|
|
"<Multi_key> <o> <e> : oe\n"
|
|
|
|
|
"<dead_circumflex> <e> : \"bar\" Y\n"
|
|
|
|
|
"<Multi_key> <a> <e> : \"æ\" ae\n"
|
|
|
|
|
"<dead_circumflex> <a> : \"baz\" Z\n"
|
|
|
|
|
"<dead_acute> <e> : \"é\" eacute\n"
|
|
|
|
|
"<Multi_key> <a> <a> <c>: \"aac\"\n"
|
|
|
|
|
"<Multi_key> <a> <a> <b>: \"aab\"\n"
|
|
|
|
|
"<Multi_key> <a> <a> <a>: \"aaa\"\n";
|
|
|
|
|
|
|
|
|
|
table = xkb_compose_table_new_from_buffer(ctx, buffer, strlen(buffer), "",
|
|
|
|
|
XKB_COMPOSE_FORMAT_TEXT_V1,
|
|
|
|
|
XKB_COMPOSE_COMPILE_NO_FLAGS);
|
|
|
|
|
assert(table);
|
|
|
|
|
|
|
|
|
|
struct xkb_compose_table_iterator *iter = xkb_compose_table_iterator_new(table);
|
|
|
|
|
|
|
|
|
|
test_eq_entry(xkb_compose_table_iterator_next(iter),
|
|
|
|
|
XKB_KEY_eacute, "é",
|
|
|
|
|
XKB_KEY_dead_acute, XKB_KEY_e, XKB_KEY_NoSymbol);
|
|
|
|
|
|
|
|
|
|
test_eq_entry(xkb_compose_table_iterator_next(iter),
|
|
|
|
|
XKB_KEY_Z, "baz",
|
|
|
|
|
XKB_KEY_dead_circumflex, XKB_KEY_a, XKB_KEY_NoSymbol);
|
|
|
|
|
|
|
|
|
|
test_eq_entry(xkb_compose_table_iterator_next(iter),
|
|
|
|
|
XKB_KEY_Y, "bar",
|
|
|
|
|
XKB_KEY_dead_circumflex, XKB_KEY_e, XKB_KEY_NoSymbol);
|
|
|
|
|
|
|
|
|
|
test_eq_entry(xkb_compose_table_iterator_next(iter),
|
|
|
|
|
XKB_KEY_X, "foo",
|
|
|
|
|
XKB_KEY_dead_circumflex, XKB_KEY_dead_circumflex, XKB_KEY_NoSymbol);
|
|
|
|
|
|
|
|
|
|
test_eq_entry(xkb_compose_table_iterator_next(iter),
|
|
|
|
|
XKB_KEY_NoSymbol, "aaa",
|
|
|
|
|
XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_a, XKB_KEY_a, XKB_KEY_NoSymbol);
|
|
|
|
|
|
|
|
|
|
test_eq_entry(xkb_compose_table_iterator_next(iter),
|
|
|
|
|
XKB_KEY_NoSymbol, "aab",
|
|
|
|
|
XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_a, XKB_KEY_b, XKB_KEY_NoSymbol);
|
|
|
|
|
|
|
|
|
|
test_eq_entry(xkb_compose_table_iterator_next(iter),
|
|
|
|
|
XKB_KEY_NoSymbol, "aac",
|
|
|
|
|
XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_a, XKB_KEY_c, XKB_KEY_NoSymbol);
|
|
|
|
|
|
|
|
|
|
test_eq_entry(xkb_compose_table_iterator_next(iter),
|
|
|
|
|
XKB_KEY_ae, "æ",
|
|
|
|
|
XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_e, XKB_KEY_NoSymbol);
|
|
|
|
|
|
|
|
|
|
test_eq_entry(xkb_compose_table_iterator_next(iter),
|
|
|
|
|
XKB_KEY_oe, "",
|
|
|
|
|
XKB_KEY_Multi_key, XKB_KEY_o, XKB_KEY_e, XKB_KEY_NoSymbol);
|
|
|
|
|
|
|
|
|
|
test_eq_entry(xkb_compose_table_iterator_next(iter),
|
|
|
|
|
XKB_KEY_NoSymbol, "foobar",
|
|
|
|
|
XKB_KEY_Ahook, XKB_KEY_x, XKB_KEY_NoSymbol);
|
|
|
|
|
|
|
|
|
|
assert (xkb_compose_table_iterator_next(iter) == NULL);
|
|
|
|
|
|
|
|
|
|
xkb_compose_table_iterator_free(iter);
|
|
|
|
|
xkb_compose_table_unref(table);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-26 09:05:05 -06:00
|
|
|
|
static void
|
2023-11-07 04:58:41 -07:00
|
|
|
|
test_decode_escape_sequences(struct xkb_context *ctx)
|
2023-09-26 09:05:05 -06:00
|
|
|
|
{
|
2023-09-26 09:05:14 -06:00
|
|
|
|
/* The following escape sequences should be ignored:
|
|
|
|
|
* • \401 overflows
|
|
|
|
|
* • \0 and \x0 produce NULL
|
|
|
|
|
*/
|
2023-11-07 04:58:41 -07:00
|
|
|
|
const char table_string_1[] = "<o> <e> : \"\\401f\\x0o\\0o\" X\n";
|
2023-09-26 09:05:05 -06:00
|
|
|
|
|
2023-11-07 04:58:41 -07:00
|
|
|
|
assert(test_compose_seq_buffer(ctx, table_string_1,
|
2023-09-26 09:05:05 -06:00
|
|
|
|
XKB_KEY_o, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol,
|
|
|
|
|
XKB_KEY_e, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "foo", XKB_KEY_X,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
2023-11-07 04:58:41 -07:00
|
|
|
|
|
|
|
|
|
/* Test various cases */
|
|
|
|
|
const char table_string_2[] =
|
|
|
|
|
"<a> : \"\\x0abcg\\\"x\" A\n" /* hexadecimal sequence has max 2 chars */
|
|
|
|
|
"<b> : \"éxyz\" B\n" /* non-ASCII (2 bytes) */
|
|
|
|
|
"<c> : \"€xyz\" C\n" /* non-ASCII (3 bytes) */
|
|
|
|
|
"<d> : \"✨xyz\" D\n" /* non-ASCII (4 bytes) */
|
|
|
|
|
"<e> : \"✨\\x0aé\\x0a€x\\\"\" E\n"
|
|
|
|
|
"<f> : \"\" F\n";
|
|
|
|
|
|
|
|
|
|
assert(test_compose_seq_buffer(ctx, table_string_2,
|
|
|
|
|
XKB_KEY_a, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "\x0a""bcg\"x", XKB_KEY_A,
|
|
|
|
|
XKB_KEY_b, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "éxyz", XKB_KEY_B,
|
|
|
|
|
XKB_KEY_c, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "€xyz", XKB_KEY_C,
|
|
|
|
|
XKB_KEY_d, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "✨xyz", XKB_KEY_D,
|
|
|
|
|
XKB_KEY_e, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "✨\x0aé\x0a€x\"", XKB_KEY_E,
|
|
|
|
|
XKB_KEY_f, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "", XKB_KEY_F,
|
|
|
|
|
XKB_KEY_NoSymbol));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
|
random_non_null_unicode_char(bool ascii)
|
|
|
|
|
{
|
|
|
|
|
if (ascii)
|
|
|
|
|
return 0x01 + (rand() % 0x80);
|
|
|
|
|
switch (rand() % 5) {
|
|
|
|
|
case 0:
|
|
|
|
|
/* U+0080..U+07FF: 2 bytes in UTF-8 */
|
|
|
|
|
return 0x80 + (rand() % 0x800);
|
|
|
|
|
case 1:
|
|
|
|
|
/* U+0800..U+FFFF: 3 bytes in UTF-8 */
|
|
|
|
|
return 0x800 + (rand() % 0x10000);
|
|
|
|
|
case 2:
|
|
|
|
|
/* U+10000..U+10FFFF: 4 bytes in UTF-8 */
|
|
|
|
|
return 0x10000 + (rand() % 0x110000);
|
|
|
|
|
default:
|
|
|
|
|
/* NOTE: Higher probability for ASCII */
|
|
|
|
|
/* U+0001..U+007F: 1 byte in UTF-8 */
|
|
|
|
|
return 0x01 + (rand() % 0x80);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
test_encode_escape_sequences(struct xkb_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
char *escaped;
|
|
|
|
|
|
|
|
|
|
/* Test empty string */
|
|
|
|
|
escaped = escape_utf8_string_literal("");
|
|
|
|
|
assert_streq_not_null("Empty string", "", escaped);
|
|
|
|
|
free(escaped);
|
|
|
|
|
|
|
|
|
|
/* Test specific ASCII characters: ", \ */
|
|
|
|
|
escaped = escape_utf8_string_literal("\"\\");
|
|
|
|
|
assert_streq_not_null("Quote and backslash", "\\\"\\\\", escaped);
|
|
|
|
|
free(escaped);
|
|
|
|
|
|
|
|
|
|
/* Test round-trip of random strings */
|
|
|
|
|
# define SAMPLE_SIZE 1000
|
|
|
|
|
# define MIN_CODE_POINT 0x0001
|
|
|
|
|
# define MAX_CODE_POINTS_COUNT 15
|
|
|
|
|
char buf[1 + MAX_CODE_POINTS_COUNT * 4];
|
|
|
|
|
for (int ascii = 1; ascii >= 0; ascii--) {
|
|
|
|
|
for (size_t s = 0; s < SAMPLE_SIZE; s++) {
|
|
|
|
|
/* Create the string */
|
|
|
|
|
size_t length = 1 + (rand() % MAX_CODE_POINTS_COUNT);
|
|
|
|
|
size_t c = 0;
|
|
|
|
|
for (size_t idx = 0; idx < length; idx++) {
|
|
|
|
|
int nbytes;
|
|
|
|
|
/* Get a random Unicode code point and encode it in UTF-8 */
|
|
|
|
|
do {
|
|
|
|
|
const uint32_t cp = random_non_null_unicode_char(ascii);
|
|
|
|
|
nbytes = utf32_to_utf8(cp, &buf[c]);
|
|
|
|
|
} while (!nbytes); /* Handle invalid code point in UTF-8 */
|
|
|
|
|
c += nbytes - 1;
|
|
|
|
|
assert(c <= sizeof(buf) - 1);
|
|
|
|
|
}
|
|
|
|
|
assert_printf(buf[c] == '\0', "NULL-terminated string\n");
|
|
|
|
|
assert_printf(strlen(buf) == c, "Contains no NULL char\n");
|
|
|
|
|
assert_printf(is_valid_utf8(buf, c),
|
|
|
|
|
"Invalid input UTF-8 string: \"%s\"\n", buf);
|
|
|
|
|
/* Escape the string */
|
|
|
|
|
escaped = escape_utf8_string_literal(buf);
|
|
|
|
|
if (!escaped)
|
|
|
|
|
break;
|
|
|
|
|
assert_printf(is_valid_utf8(escaped, strlen(escaped)),
|
|
|
|
|
"Invalid input UTF-8 string: %s\n", escaped);
|
|
|
|
|
char *string_literal = asprintf_safe("\"%s\"", escaped);
|
|
|
|
|
if (!string_literal) {
|
|
|
|
|
free(escaped);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* Unescape the string */
|
|
|
|
|
char *unescaped = parse_string_literal(ctx, string_literal);
|
|
|
|
|
assert_streq_not_null("Escaped string", buf, unescaped);
|
|
|
|
|
free(unescaped);
|
|
|
|
|
free(string_literal);
|
|
|
|
|
free(escaped);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
# undef SAMPLE_SIZE
|
|
|
|
|
# undef MIN_CODE_POINT
|
|
|
|
|
# undef MAX_CODE_POINTS_COUNT
|
2023-09-26 09:05:05 -06:00
|
|
|
|
}
|
|
|
|
|
|
2013-10-27 12:37:27 -06:00
|
|
|
|
int
|
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
struct xkb_context *ctx;
|
|
|
|
|
|
|
|
|
|
ctx = test_get_context(CONTEXT_NO_FLAG);
|
|
|
|
|
assert(ctx);
|
|
|
|
|
|
2023-11-07 04:58:41 -07:00
|
|
|
|
/* Initialize pseudo-random generator with program arg or current time */
|
|
|
|
|
int seed;
|
|
|
|
|
if (argc == 2) {
|
|
|
|
|
seed = atoi(argv[1]);
|
|
|
|
|
} else {
|
|
|
|
|
seed = time(NULL);
|
|
|
|
|
}
|
|
|
|
|
fprintf(stderr, "Seed for the pseudo-random generator: %d\n", seed);
|
|
|
|
|
srand(seed);
|
|
|
|
|
|
2023-06-26 04:19:49 -06:00
|
|
|
|
/*
|
|
|
|
|
* Ensure no environment variables but “top_srcdir” is set. This ensures
|
|
|
|
|
* that user Compose file paths are unset before the tests and set
|
2023-11-07 04:58:41 -07:00
|
|
|
|
* explicitly when necessary.
|
2023-06-26 04:19:49 -06:00
|
|
|
|
*/
|
|
|
|
|
#ifdef __linux__
|
|
|
|
|
const char *srcdir = getenv("top_srcdir");
|
|
|
|
|
clearenv();
|
|
|
|
|
setenv("top_srcdir", srcdir, 1);
|
|
|
|
|
#else
|
|
|
|
|
unsetenv("XCOMPOSEFILE");
|
|
|
|
|
unsetenv("XDG_CONFIG_HOME");
|
|
|
|
|
unsetenv("HOME");
|
|
|
|
|
unsetenv("XLOCALEDIR");
|
|
|
|
|
#endif
|
|
|
|
|
|
2023-10-29 00:20:29 -06:00
|
|
|
|
test_compose_utf8_bom(ctx);
|
2023-10-30 07:50:00 -06:00
|
|
|
|
test_invalid_encodings(ctx);
|
2013-10-27 12:37:27 -06:00
|
|
|
|
test_seqs(ctx);
|
|
|
|
|
test_conflicting(ctx);
|
|
|
|
|
test_XCOMPOSEFILE(ctx);
|
2021-05-22 11:07:06 -06:00
|
|
|
|
test_from_locale(ctx);
|
2013-10-27 12:37:27 -06:00
|
|
|
|
test_state(ctx);
|
2014-10-14 01:53:38 -06:00
|
|
|
|
test_modifier_syntax(ctx);
|
2014-10-14 02:47:25 -06:00
|
|
|
|
test_include(ctx);
|
2022-05-14 02:11:32 -06:00
|
|
|
|
test_override(ctx);
|
2023-09-25 03:41:48 -06:00
|
|
|
|
test_traverse(ctx);
|
2023-11-07 04:58:41 -07:00
|
|
|
|
test_decode_escape_sequences(ctx);
|
|
|
|
|
test_encode_escape_sequences(ctx);
|
2013-10-27 12:37:27 -06:00
|
|
|
|
|
|
|
|
|
xkb_context_unref(ctx);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|