chore(linux): Add integration tests for ibus-keyman

This change adds integration/regression tests for ibus-keyman. It
re-uses the unit tests that we have for core.

Also modifies one test so that the context doesn't use characters
used in the keyboard. This makes it easier to setup in ibus-keyman
tests.
This commit is contained in:
Eberhard Beilharz 2021-08-31 08:25:38 +02:00
parent db5b9db23b
commit cdee19e953
No known key found for this signature in database
GPG key ID: 64A39A9E98B53105
14 changed files with 2179 additions and 9 deletions

View file

@ -1,5 +1,6 @@
[flake8]
max-line-length = 120
indent-size = 2
ignore =
E402, # module level import not at top of file
W504, # line break after binary operator

View file

@ -11,3 +11,9 @@ VERSION.md
shellHelperFunctions.sh
src/rust/keyman_keyboard_processor/target/
# The following files/subdirs are generated by autotools when building ibus-keyman.
# They can be removed after we convert ibus-keyman to use meson.
.deps/
.dirstamp
*.o

View file

@ -6,14 +6,14 @@ c 4. Two deadkeys in a row in context dk(4a) dk(4b) + BKSP = nul
c 5. One char and two deadkeys in context 'a' dk(5a) dk(5b) 'b' + BKSP = 'a'
c 6. One char and two deadkeys and one char and two deadkeys in context 'a' dk(6a) dk(6b) 'b' dk(6c) dk(6d) + BKSP = 'a'
c keys: [K_1][K_BKSP][K_2][K_BKSP][K_3][K_BKSP][K_4][K_BKSP][K_5][K_BKSP][K_6][K_BKSP]
c expected: 12aa
c context: 1234
c expected: 78aa
c context: 7890
store(&VERSION) '9.0'
begin unicode > use(main)
group(main) using keys
group(main) using keys
+ '1' > dk(1)
+ '2' > 'a' dk(2)

View file

@ -20,6 +20,7 @@
SUBDIRS = \
src \
tests \
icons \
m4 \
po \

View file

@ -100,6 +100,7 @@ Makefile
src/Makefile
src/keyman.xml.in
src/keyman-version.h
tests/Makefile
icons/Makefile
m4/Makefile
])

View file

@ -162,13 +162,13 @@ static void
ibus_keyman_engine_class_init (IBusKeymanEngineClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass);
IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass);
parent_class = (IBusEngineClass *) g_type_class_peek_parent (klass);
parent_class = (IBusEngineClass *) g_type_class_peek_parent (klass);
object_class->constructor = ibus_keyman_engine_constructor;
ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_keyman_engine_destroy;
ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_keyman_engine_destroy;
engine_class->process_key_event = ibus_keyman_engine_process_key_event;
@ -586,8 +586,7 @@ process_backspace_action(
return FALSE;
} else {
g_message(
"DAR: ibus_keyman_engine_process_key_event - client_capabilities=%x, %x", engine->client_capabilities,
IBUS_CAP_SURROUNDING_TEXT);
"DAR: process_backspace_action - client_capabilities=%x, %x", engine->client_capabilities, IBUS_CAP_SURROUNDING_TEXT);
if ((engine->client_capabilities & IBUS_CAP_SURROUNDING_TEXT) != 0) {
g_message("deleting surrounding text 1 char");

2
linux/ibus-keyman/tests/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
.libs/
ibus-keyman-tests

View file

@ -0,0 +1,112 @@
# vim:set noet ts=4:
#
# ibus-keyman unit tests
#
# Copyright (c) 2021 SIL International
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
AUTOMAKE_OPTIONS = subdir-objects
AM_CFLAGS = \
@IBUS_CFLAGS@ \
@GTK_CFLAGS@ \
@JSON_GLIB_CFLAGS@ \
@X11_CFLAGS@ \
@KEYMAN_PROC_CFLAGS@ \
-DPKGDATADIR=\"$(pkgdatadir)\" \
$(NULL)
AM_LDFLAGS = \
@IBUS_LIBS@ \
@GTK_LIBS@ \
@X11_LIBS@ \
@JSON_GLIB_LIBS@ \
@KEYMAN_PROC_LIBS@ \
$(NULL)
libexec_PROGRAMS = ibus-keyman-tests
ibus_keyman_tests_SOURCES = \
ibusimcontext.c \
ibusimcontext.h \
testfixture.cpp \
testmodule.c \
testmodule.h \
../../../common/core/desktop/src/json.cpp \
../../../common/core/desktop/src/keyboard.cpp \
../../../common/core/desktop/src/kmx/kmx_actions.cpp \
../../../common/core/desktop/src/kmx/kmx_actions.h \
../../../common/core/desktop/src/kmx/kmx_base.h \
../../../common/core/desktop/src/kmx/kmx_capslock.cpp \
../../../common/core/desktop/src/kmx/kmx_consts.cpp \
../../../common/core/desktop/src/kmx/kmx_context.cpp \
../../../common/core/desktop/src/kmx/kmx_context.h \
../../../common/core/desktop/src/kmx/kmx_debug.cpp \
../../../common/core/desktop/src/kmx/kmx_debugger.cpp \
../../../common/core/desktop/src/kmx/kmx_debugger.h \
../../../common/core/desktop/src/kmx/kmx_environment.cpp \
../../../common/core/desktop/src/kmx/kmx_environment.h \
../../../common/core/desktop/src/kmx/kmx_file.cpp \
../../../common/core/desktop/src/kmx/kmx_file.h \
../../../common/core/desktop/src/kmx/kmx_modifiers.cpp \
../../../common/core/desktop/src/kmx/kmx_options.cpp \
../../../common/core/desktop/src/kmx/kmx_options.h \
../../../common/core/desktop/src/kmx/kmx_processevent.cpp \
../../../common/core/desktop/src/kmx/kmx_processevent.h \
../../../common/core/desktop/src/kmx/kmx_processor.cpp \
../../../common/core/desktop/src/kmx/kmx_processor.hpp \
../../../common/core/desktop/src/kmx/kmx_xstring.cpp \
../../../common/core/desktop/src/kmx/kmx_xstring.h \
../../../common/core/desktop/src/option.cpp \
../../../common/core/desktop/src/state.cpp \
../../../common/core/desktop/src/utfcodec.cpp \
../../../common/core/desktop/tests/unit/kmx/kmx_test_source.cpp \
../../../common/core/desktop/tests/unit/kmx/kmx_test_source.hpp \
$(NULL)
ibus_keyman_tests_CFLAGS = \
@IBUS_CFLAGS@ \
@GTK_CFLAGS@ \
@X11_CFLAGS@ \
@JSON_GLIB_CFLAGS@ \
@KEYMAN_PROC_CFLAGS@ \
-DPKGDATADIR=\"$(pkgdatadir)\" \
-I../../../common/core/desktop/src \
-I../../../common/core/desktop/src/kmx \
-I../../../common/core/desktop/tests/unit/kmx \
-I../src \
$(NULL)
ibus_keyman_tests_CPPFLAGS = \
@IBUS_CFLAGS@ \
@GTK_CFLAGS@ \
@X11_CFLAGS@ \
@JSON_GLIB_CFLAGS@ \
@KEYMAN_PROC_CFLAGS@ \
-DPKGDATADIR=\"$(pkgdatadir)\" \
-I../../../common/core/desktop/src \
-I../../../common/core/desktop/src/kmx \
-I../../../common/core/desktop/tests/unit/kmx \
-I../src \
$(NULL)
ibus_keyman_tests_LDFLAGS = \
@GTK_LIBS@ \
@IBUS_LIBS@ \
@X11_LIBS@ \
@JSON_GLIB_LIBS@ \
@KEYMAN_PROC_LIBS@ \
$(NULL)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,83 @@
/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
/* vim:set et ts=4: */
/* ibus - The Input Bus
* Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#ifndef __IBUSIMCONTEXT_H__
#define __IBUSIMCONTEXT_H__
#include <gtk/gtk.h>
/*
* Type macros.
*/
#define IBUS_TYPE_IM_CONTEXT \
(ibus_im_context_get_type ())
#define IBUS_IM_CONTEXT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_IM_CONTEXT, IBusIMContext))
#define IBUS_IM_CONTEXT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_IM_CONTEXT, IBusIMContextClass))
#define IBUS_IS_IM_CONTEXT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_IM_CONTEXT))
#define IBUS_IS_IM_CONTEXT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_IM_CONTEXT))
#define IBUS_IM_CONTEXT_GET_CLASS(obj) \
(G_TYPE_CHECK_GET_CLASS ((obj), IBUS_TYPE_IM_CONTEXT, IBusIMContextClass))
G_BEGIN_DECLS
typedef struct _IBusIMContext IBusIMContext;
typedef struct _IBusIMContextClass IBusIMContextClass;
typedef struct _IBusIMContextPrivate IBusIMContextPrivate;
GType ibus_im_context_get_type (void);
IBusIMContext
*ibus_im_context_new (void);
void ibus_im_context_register_type
(GTypeModule *type_module);
void ibus_im_context_shutdown
(void);
const gchar
*ibus_im_context_get_ic (IBusIMContext *context);
void ibus_im_context_set_ic (IBusIMContext *context,
const gchar *ic);
void ibus_im_context_enable (IBusIMContext *context);
void ibus_im_context_disable (IBusIMContext *context);
void ibus_im_context_commit_string
(IBusIMContext *context,
const gchar *string);
void ibus_im_context_update_preedit
(IBusIMContext *context,
const gchar *string,
PangoAttrList *attrs,
gint cursor_pos,
gboolean visible);
void ibus_im_context_show_preedit
(IBusIMContext *context);
void ibus_im_context_hide_preedit
(IBusIMContext *context);
void ibus_im_test_set_thread_loop(IBusIMContext *context, GMainLoop *loop);
void ibus_im_test_set_text(IBusIMContext *context, const gchar *text);
const gchar *ibus_im_test_get_text(IBusIMContext *context);
void ibus_im_test_clear_text(IBusIMContext *context);
G_END_DECLS
#endif // __IBUSIMCONTEXT_H__

View file

@ -0,0 +1,29 @@
#!/bin/bash
BASEDIR=$(dirname $0)
TESTDIR=${XDG_DATA_HOME:-$HOME/.local/share}/keyman/test_kmx
if [ "$DISPLAY" == ":0" ]; then
echo "Please start xephyr and set the DISPLAY environment variable before running these tests."
exit 1
fi
if [ ! -d $TESTDIR ]; then
ln -sf $BASEDIR/../../../common/core/desktop/build/arch/debug/tests/unit/kmx $TESTDIR
fi
if [ $# -gt 0 ]; then
TESTFILES=$@
else
pushd $TESTDIR
TESTFILES=(*.kmx)
popd
fi
export GSETTINGS_BACKEND=keyfile
ibus-daemon --panel=disable --config=/usr/libexec/ibus-memconf > /tmp/ibus-daemon.log 2>&1 &
../src/ibus-engine-keyman > /tmp/ibus-engine-keyman.log 2>&1 &
sleep 1s
./ibus-keyman-tests -k --tap --directory $TESTDIR "${TESTFILES[@]}"

View file

@ -0,0 +1,352 @@
#include <glib-object.h>
#include <glib.h>
#include <ibus.h>
#include <iostream>
#include <keyman/keyboardprocessor.h>
#include <kmx/kmx_processevent.h>
#include <linux/input-event-codes.h>
#include <list>
#include <memory>
#include <stack>
#include <stdexcept>
#include <string>
#include "ibusimcontext.h"
#include "keycodes.h"
#include "kmx_test_source.hpp"
#include "testmodule.h"
typedef struct {
IBusBus *bus;
GtkIMContext *context;
IBusIMContext *ibuscontext;
} IBusKeymanTestsFixture;
static gboolean loaded = FALSE;
static GdkWindow *window = NULL;
static GMainLoop *thread_loop = NULL;
static GTypeModule *module = NULL;
static void
module_register(GTypeModule *module) {
ibus_im_context_register_type(module);
}
void
destroy(GtkWidget *widget, gpointer data) {
gtk_main_quit();
}
static void
ibus_keyman_tests_fixture_set_up(IBusKeymanTestsFixture *fixture, gconstpointer user_data) {
IBusIMContextClass *classClass;
if (!window) {
GtkWidget *widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(widget, "destroy", G_CALLBACK(destroy), NULL);
window = gtk_widget_get_window(widget);
}
if (!loaded) {
ibus_init();
module = test_module_new(module_register);
g_assert_nonnull(module);
/* Not loaded until we call ref for the first time */
classClass = static_cast<IBusIMContextClass *>(g_type_class_peek(IBUS_TYPE_IM_CONTEXT));
g_assert_null(classClass);
loaded = TRUE;
}
fixture->bus = ibus_bus_new();
}
static void
ibus_keyman_tests_fixture_tear_down(IBusKeymanTestsFixture *fixture, gconstpointer user_data) {
if (thread_loop) {
g_main_loop_unref(thread_loop);
thread_loop = NULL;
}
g_clear_object(&fixture->bus);
g_clear_object(&fixture->context);
}
static void
switch_keyboard(IBusKeymanTestsFixture *fixture, const gchar *keyboard) {
ibus_bus_set_global_engine(fixture->bus, keyboard);
IBusEngineDesc *desc = ibus_bus_get_global_engine(fixture->bus);
g_object_ref_sink(desc);
// g_debug("Switched to engine: '%s'", ibus_engine_desc_get_name(desc));
g_assert_cmpstr(keyboard, ==, ibus_engine_desc_get_name(desc));
g_clear_object(&desc);
if (thread_loop) {
g_main_loop_unref(thread_loop);
thread_loop = NULL;
}
if (fixture->context) {
g_clear_object(&fixture->context);
}
fixture->ibuscontext = ibus_im_context_new();
fixture->context = (GtkIMContext *)fixture->ibuscontext;
thread_loop = g_main_loop_new(NULL, TRUE);
ibus_im_test_set_thread_loop(fixture->ibuscontext, thread_loop);
}
template <typename... Args>
std::string
string_format(const std::string &format, Args... args) {
// from https://stackoverflow.com/a/26221725/487503
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
if (size_s <= 0) {
throw std::runtime_error("Error during formatting.");
}
auto size = static_cast<size_t>(size_s);
auto buf = std::make_unique<char[]>(size);
std::snprintf(buf.get(), size, format.c_str(), args...);
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
}
static unsigned short vk_to_keycode(unsigned short vk) {
for (int i = 0; i < sizeof(keycode_to_vk); i++) {
if (keycode_to_vk[i] == vk)
return i;
}
return -1;
}
#define KEYMAN_LCTRL 29 // 0x1D
#define KEYMAN_LALT 56 // 0x38
#define KEYMAN_RCTRL 97 // 0x61
#define KEYMAN_RALT 100 // 0x64
static guint
km_modifiers_to_ibus(km::tests::KmxTestSource& test_source, km::tests::key_event event) {
guint result = 0;
auto state = event.modifier_state | test_source.caps_lock_state();
if (state & KM_KBP_MODIFIER_SHIFT) {
result |= IBUS_SHIFT_MASK;
}
if (state & KM_KBP_MODIFIER_ALT) {
result |= IBUS_MOD1_MASK;
}
if (state & KM_KBP_MODIFIER_RALT) {
if (event.vk == KEYMAN_RALT)
result |= IBUS_MOD1_MASK;
else
result |= IBUS_MOD5_MASK;
}
if (state & KM_KBP_MODIFIER_LALT) {
result |= IBUS_MOD1_MASK;
}
if (state & KM_KBP_MODIFIER_CTRL) {
result |= IBUS_CONTROL_MASK;
}
if (state & KM_KBP_MODIFIER_RCTRL) {
result |= IBUS_CONTROL_MASK;
}
if (state & KM_KBP_MODIFIER_LCTRL) {
result |= IBUS_CONTROL_MASK;
}
if (state & KM_KBP_MODIFIER_CAPS) {
result |= IBUS_LOCK_MASK;
}
return result;
}
typedef struct {
guint modifiers_keydown;
guint modifiers_keyup;
guint keyval;
guint16 keycode;
guint is_modifier;
} gdk_key_event;
static gdk_key_event
get_key_event_for_modifier(guint modifier, guint & prev_modifiers, guint keyval, guint keycode) {
gdk_key_event event;
event.modifiers_keydown = prev_modifiers;
event.modifiers_keyup = prev_modifiers | modifier | IBUS_RELEASE_MASK;
prev_modifiers |= modifier;
event.keyval = keyval;
event.keycode = keycode;
event.is_modifier = 1;
return event;
}
static std::list<gdk_key_event>
get_involved_keys(km::tests::KmxTestSource &test_source, km::tests::key_event test_event) {
guint modifiers = 0;
if (test_source.caps_lock_state()) {
modifiers = IBUS_LOCK_MASK;
}
// process all modifiers
std::list<gdk_key_event> result;
if (test_event.modifier_state & KM_KBP_MODIFIER_SHIFT) {
// we don't distinguish between R and L Shift
result.push_back(get_key_event_for_modifier(IBUS_SHIFT_MASK, modifiers, GDK_KEY_Shift_L, KEY_LEFTSHIFT));
}
if (test_event.modifier_state & KM_KBP_MODIFIER_ALT || test_event.modifier_state & KM_KBP_MODIFIER_LALT) {
result.push_back(get_key_event_for_modifier(IBUS_MOD1_MASK, modifiers, GDK_KEY_Alt_L, KEY_LEFTALT));
}
if (test_event.modifier_state & KM_KBP_MODIFIER_RALT) {
if (test_event.vk == KEYMAN_RALT)
result.push_back(get_key_event_for_modifier(IBUS_MOD1_MASK, modifiers, GDK_KEY_Alt_R, KEY_RIGHTALT));
else
result.push_back(get_key_event_for_modifier(IBUS_MOD5_MASK, modifiers, GDK_KEY_Alt_R, KEY_RIGHTALT));
}
if (test_event.modifier_state & KM_KBP_MODIFIER_CTRL || test_event.modifier_state & KM_KBP_MODIFIER_LCTRL) {
result.push_back(get_key_event_for_modifier(IBUS_CONTROL_MASK, modifiers, GDK_KEY_Control_L, KEY_LEFTCTRL));
}
if (test_event.modifier_state & KM_KBP_MODIFIER_RCTRL) {
result.push_back(get_key_event_for_modifier(IBUS_CONTROL_MASK, modifiers, GDK_KEY_Control_R, KEY_RIGHTCTRL));
}
// process key
guint keyval = (test_event.modifier_state & KM_KBP_MODIFIER_SHIFT) ? gdk_keyval_to_upper(test_event.vk)
: gdk_keyval_to_lower(test_event.vk);
gdk_key_event event;
event.modifiers_keydown = modifiers;
event.modifiers_keyup = modifiers | IBUS_RELEASE_MASK;
event.keyval = keyval;
event.keycode = vk_to_keycode(test_event.vk);
event.is_modifier = 0;
result.push_back(event);
return result;
}
static std::list<km::tests::key_event>
get_context_keys(std::u16string context) {
std::list<km::tests::key_event> result;
for (auto c = context.begin(); c != context.end(); c++) {
gchar utf8[6];
if (g_unichar_to_utf8((gunichar)*c, utf8) != 1 || utf8[0] < 32 || utf8[0] > 127) {
g_assertion_message_cmpstr(
G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, "We can only deal with ASCII characters in the context", utf8, "", "");
}
result.push_back(
{km::kbp::kmx::s_char_to_vkey[(int)utf8[0] - 32].vk,
(uint16_t)(km::kbp::kmx::s_char_to_vkey[(int)utf8[0] - 32].shifted ? KM_KBP_MODIFIER_SHIFT : 0)});
}
return result;
}
static void
press_keys(IBusKeymanTestsFixture *fixture, km::tests::KmxTestSource & test_source,km::tests::key_event key_event) {
auto involved_keys = get_involved_keys(test_source, key_event);
// press and hold the individual modifier keys, finally the actual key
std::stack<GdkEventKey> keys_to_release;
for (auto key = involved_keys.begin(); key != involved_keys.end(); key++) {
GdkEventKey keyEvent = {
.type = GDK_KEY_PRESS,
.window = window,
.send_event = 0,
.time = 0,
.state = key->modifiers_keydown,
.keyval = key->keyval,
.length = 0,
.string = NULL,
.hardware_keycode = key->keycode,
.group = 0,
.is_modifier = key->is_modifier};
gtk_im_context_filter_keypress(fixture->context, &keyEvent);
keyEvent.type = GDK_KEY_RELEASE;
keyEvent.state = key->modifiers_keyup;
keys_to_release.push(keyEvent);
}
// then release the keys in reverse order
while (!keys_to_release.empty()) {
auto keyEvent = keys_to_release.top();
gtk_im_context_filter_keypress(fixture->context, &keyEvent);
keys_to_release.pop();
}
}
static void test_source(IBusKeymanTestsFixture *fixture, gconstpointer user_data) {
auto sourcefile = string_format("%s.kmn", (char *)user_data);
auto kmxfile = string_format("und:%s.kmx", (char*)user_data);
km::tests::KmxTestSource test_source;
std::string keys = "";
std::u16string expected = u"", context = u"";
km::tests::kmx_options options;
bool expected_beep = false;
g_assert_cmpint(test_source.load_source(sourcefile.c_str(), keys, expected, context, options, expected_beep), ==, 0);
switch_keyboard(fixture, kmxfile.c_str());
auto contextKeys = get_context_keys(context);
for (auto k = contextKeys.begin(); k != contextKeys.end(); k++) {
press_keys(fixture, test_source, *k);
}
gtk_im_context_focus_in(fixture->context);
for (auto p = test_source.next_key(keys); p.vk != 0; p = test_source.next_key(keys)) {
press_keys(fixture, test_source, p);
}
auto expectedText = g_utf16_to_utf8((gunichar2*)expected.c_str(), expected.length(), NULL, NULL, NULL);
g_assert_cmpstr(ibus_im_test_get_text(fixture->ibuscontext), ==, expectedText);
}
void
print_usage() {
printf("Usage: %s --directory <keyboarddir> test1 [test2 ...]", g_get_prgname());
}
int
main(int argc, char *argv[]) {
gtk_init(&argc, &argv);
g_test_init(&argc, &argv, NULL);
g_test_set_nonfatal_assertions();
if (argc < 4) {
print_usage();
return 1;
}
if (strcmp(argv[1], "--directory") != 0) {
print_usage();
return 2;
}
char *directory = argv[2];
int nTests = argc - 3;
char **tests = &argv[3];
// Add tests
for (int i = 0; i < nTests; i++)
{
auto filename = tests[i];
if (strstr(filename, ".kmx") || strstr(filename, ".kmn")) {
filename[strlen(filename) - 4] = '\0';
}
auto file = g_file_new_for_commandline_arg(filename);
auto testfilebase = g_file_get_basename(file);
auto testname = g_string_new(NULL);
g_string_append_printf(testname, "/%s", testfilebase);
auto testfile = g_file_new_build_filename(directory, testfilebase, NULL);
g_test_add(
testname->str, IBusKeymanTestsFixture, g_file_get_parse_name(testfile), ibus_keyman_tests_fixture_set_up, test_source,
ibus_keyman_tests_fixture_tear_down);
g_object_unref(file);
g_object_unref(testfile);
g_string_free(testname, TRUE);
}
// Run tests
int retVal = g_test_run();
test_module_unuse(module);
return retVal;
}

View file

@ -0,0 +1,64 @@
#include "ibusimcontext.h"
#include <linux/input-event-codes.h>
#include <glib-object.h>
#include <glib.h>
#include <ibus.h>
#include "testmodule.h"
static gboolean
test_module_load(GTypeModule *module) {
TestModule *test_module = TEST_MODULE(module);
test_module->register_func(module);
return TRUE;
}
static void
test_module_unload(GTypeModule *module) {
}
static void
test_module_class_init(TestModuleClass *class) {
GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS(class);
module_class->load = test_module_load;
module_class->unload = test_module_unload;
}
static GType
test_module_get_type(void) {
static GType object_type = 0;
if (!object_type) {
static const GTypeInfo object_info = {
sizeof(TestModuleClass), (GBaseInitFunc)NULL,
(GBaseFinalizeFunc)NULL, (GClassInitFunc)test_module_class_init,
(GClassFinalizeFunc)NULL, NULL,
sizeof(TestModule), 0,
(GInstanceInitFunc)NULL, NULL,
};
object_type = g_type_register_static(G_TYPE_TYPE_MODULE, "TestModule", &object_info, 0);
}
return object_type;
}
GTypeModule *
test_module_new(TestModuleRegisterFunc register_func) {
TestModule *test_module = g_object_new(TEST_TYPE_MODULE, NULL);
GTypeModule *module = G_TYPE_MODULE(test_module);
test_module->register_func = register_func;
/* Register the types initially */
g_type_module_set_name(module, "FOO");
g_type_module_use(module);
//g_type_module_unuse(module);
return G_TYPE_MODULE(module);
}
void
test_module_unuse(GTypeModule *test_module) {
g_type_module_unuse(test_module);
}

View file

@ -0,0 +1,33 @@
#ifndef __TESTMODULE__
#define __TESTMODULE__
G_BEGIN_DECLS
typedef struct _TestModule TestModule;
typedef struct _TestModuleClass TestModuleClass;
#define TEST_TYPE_MODULE (test_module_get_type())
#define TEST_MODULE(module) (G_TYPE_CHECK_INSTANCE_CAST((module), TEST_TYPE_MODULE, TestModule))
#define TEST_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST((class), TEST_TYPE_MODULE, TestModuleClass))
#define TEST_IS_MODULE(module) (G_TYPE_CHECK_INSTANCE_TYPE((module), TEST_TYPE_MODULE))
#define TEST_IS_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE((class), TEST_TYPE_MODULE))
#define TEST_MODULE_GET_CLASS(module) (G_TYPE_INSTANCE_GET_CLASS((module), TEST_TYPE_MODULE, TestModuleClass))
typedef void (*TestModuleRegisterFunc)(GTypeModule* module);
struct _TestModule {
GTypeModule parent_instance;
TestModuleRegisterFunc register_func;
};
struct _TestModuleClass {
GTypeModuleClass parent_class;
};
static GType test_module_get_type(void);
GTypeModule* test_module_new(TestModuleRegisterFunc register_func);
void test_module_unuse(GTypeModule* test_module);
G_END_DECLS
#endif /* __TESTMODULE__ */