From 20d2ed5aaf3489bc73b2e93323f8db734f0dbfca Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Mon, 18 Sep 2023 11:48:41 +0200 Subject: [PATCH] =?UTF-8?q?refactor(linux):=20Add=20more=20tests=20for=20k?= =?UTF-8?q?eymanutil.c=20=F0=9F=8F=98=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- linux/ibus-keyman/src/keymanutil.c | 325 ++++----- linux/ibus-keyman/src/keymanutil_internal.h | 39 ++ linux/ibus-keyman/src/test/keymanutil_tests.c | 641 +++++++++++++++++- linux/ibus-keyman/src/test/meson.build | 4 +- linux/ibus-keyman/src/test/run-tests.sh | 2 +- linux/ibus-keyman/src/test/setup-tests.sh | 2 +- .../src/test/{ => testdata}/kmp.json | 534 +++++++-------- linux/ibus-keyman/src/test/testdata/kmp1.json | 123 ++++ linux/ibus-keyman/src/test/testdata/kmp2.json | 127 ++++ 9 files changed, 1340 insertions(+), 457 deletions(-) create mode 100644 linux/ibus-keyman/src/keymanutil_internal.h rename linux/ibus-keyman/src/test/{ => testdata}/kmp.json (95%) create mode 100644 linux/ibus-keyman/src/test/testdata/kmp1.json create mode 100644 linux/ibus-keyman/src/test/testdata/kmp2.json diff --git a/linux/ibus-keyman/src/keymanutil.c b/linux/ibus-keyman/src/keymanutil.c index 9eb42c173b2..0906bae3860 100644 --- a/linux/ibus-keyman/src/keymanutil.c +++ b/linux/ibus-keyman/src/keymanutil.c @@ -58,15 +58,16 @@ #include #include "bcp47util.h" -#include "keymanutil.h" #include "kmpdetails.h" #include "keyman-version.h" +#include "keymanutil.h" +#include "keymanutil_internal.h" #define N_(text) text // change to keyman_get_kmpdirs_fromdir // returns list of directories with kmp.json -GList * keyman_get_kmpdirs_fromdir( GList *keyboard_list, const gchar * path) +GList * keyman_get_kmpdirs_fromdir(GList *kmpdir_list, const gchar * path) { DIR *dir = opendir(path); @@ -80,13 +81,13 @@ GList * keyman_get_kmpdirs_fromdir( GList *keyboard_list, const gchar * path) if (S_ISDIR(filestat.st_mode)) { if(g_strcmp0(file->d_name, ".") != 0 && g_strcmp0(file->d_name, "..") != 0) - keyboard_list = keyman_get_kmpdirs_fromdir(keyboard_list, absfn); + kmpdir_list = keyman_get_kmpdirs_fromdir(kmpdir_list, absfn); } // Looking for kmp.json else if (S_ISREG(filestat.st_mode) && g_strcmp0(file->d_name, "kmp.json") == 0) { g_message("adding kmp path %s", path); - keyboard_list=g_list_append(keyboard_list, g_strdup(path)); + kmpdir_list=g_list_append(kmpdir_list, g_strdup(path)); } g_free(absfn); @@ -94,7 +95,7 @@ GList * keyman_get_kmpdirs_fromdir( GList *keyboard_list, const gchar * path) } closedir(dir); } - return keyboard_list; + return kmpdir_list; } gchar * keyman_get_icon_file(const gchar *kmx_file) @@ -115,7 +116,7 @@ gchar * keyman_get_icon_file(const gchar *kmx_file) return full_path_to_icon_file; } -static IBusEngineDesc * +IBusEngineDesc * ibus_keyman_engine_desc_new (gchar * file_name, gchar *name, gchar *description, @@ -152,127 +153,157 @@ ibus_keyman_engine_desc_new (gchar * file_name, return engine_desc; } -GList * -ibus_keyman_add_engines(GList * engines, GList * kmpdir_list) -{ - GList *p, *k, *l, *e; - - for (p=kmpdir_list; p != NULL; p = p->next) { - gchar * kmp_dir = (gchar *) p->data; - - kmp_details *details = g_new0(kmp_details, 1); - get_kmp_details(kmp_dir, details); - - for (k=details->keyboards; k != NULL; k = k->next) { - kmp_keyboard *keyboard = (kmp_keyboard *) k->data; - gboolean alreadyexists = FALSE; - - for (e=engines; e != NULL && alreadyexists == FALSE; e = e->next) { - IBusEngineDesc *engine_desc = (IBusEngineDesc *) e->data; - const gchar *version = ibus_engine_desc_get_version(engine_desc); - const gchar *engine_name = ibus_engine_desc_get_name(engine_desc); - gchar *kmx_file = g_path_get_basename(engine_name); - if (g_strcmp0(kmx_file, keyboard->kmx_file) == 0 && g_strcmp0(version, keyboard->version) >= 0) { - alreadyexists = TRUE; - g_debug("keyboard %s already exists at version %s which is newer or same as %s", kmx_file, version, keyboard->version); - } - g_free(kmx_file); - } +IBusEngineDesc * +get_engine_for_language( + kmp_keyboard *keyboard, + kmp_info *info, + keyboard_details *kbd_details, + gchar *kmp_dir, + gchar *lang_id, + gchar *lang_name) { + IBusEngineDesc* engine_desc = NULL; + if (!lang_id || !strlen(lang_id)) + return engine_desc; - if (!alreadyexists) { - gchar *abs_kmx = g_strjoin("/", kmp_dir, keyboard->kmx_file, NULL); - gchar *json_file = g_strjoin(".", keyboard->id, "json", NULL); - keyboard_details *kbd_details = g_new0(keyboard_details, 1); - get_keyboard_details(kmp_dir, json_file, kbd_details); - g_free(json_file); - - if (keyboard->languages != NULL) { - for (l=keyboard->languages; l != NULL; l = l->next) { - kmp_language *language = (kmp_language *) l->data; - if (language->id != NULL) { - int capacity = 255; - gchar *name_with_lang = NULL; - gchar *minimized_tag = g_new0(gchar, capacity); - int result = bcp47_minimize(language->id, minimized_tag, capacity); - if (result < 0) { - g_strlcpy(minimized_tag, language->id, capacity); - } - - gchar *lang_code = g_new0(gchar, capacity); - if (!bcp47_get_language_code(minimized_tag, lang_code, capacity)) { - g_strlcpy(lang_code, minimized_tag, capacity); - } - - // If ibus doesn't know about the language then append the - // language name to the keyboard name - if (language->name != NULL) { - gchar *ibus_lang = ibus_get_untranslated_language_name(lang_code); - g_debug("%s: untranslated ibus language for %s: %s", __FUNCTION__, minimized_tag, ibus_lang); - if (g_strcmp0(ibus_lang, "Other") == 0) { - name_with_lang = g_strjoin(" - ", keyboard->name, language->name, NULL); - } - g_free(ibus_lang); - } - - gchar *id_with_lang = g_strjoin(":", minimized_tag, abs_kmx, NULL); - - g_message("adding engine %s", id_with_lang); - engines = g_list_append( - engines, - ibus_keyman_engine_desc_new( - id_with_lang, // lang:kmx full path - name_with_lang ? name_with_lang : keyboard->name, // longname - kbd_details->description, // description - details->info.copyright, // copyright if available - lang_code, // language, most are ignored by ibus except major languages - kbd_details->license, // license - details->info.author_desc, // author name only, not email - keyman_get_icon_file(abs_kmx), // icon full path - "us", // layout defaulting to us (en-US) - keyboard->version)); - g_free(lang_code); - g_free(minimized_tag); - g_free(id_with_lang); - g_free(name_with_lang); - } - } - } - else { - g_message("adding engine %s", abs_kmx); - engines = g_list_append (engines, - ibus_keyman_engine_desc_new (abs_kmx, // kmx full path - keyboard->name, // longname - kbd_details->description, // description - details->info.copyright, // copyright if available - NULL, // language, most are ignored by ibus except major languages - kbd_details->license, // license - details->info.author_desc, // author name only, not email - keyman_get_icon_file(abs_kmx), // icon full path - "us", // layout defaulting to us (en-US) - keyboard->version)); - } - free_keyboard_details(kbd_details); - g_free(kbd_details); - g_free(abs_kmx); - } + int capacity = 255; + gchar *name_with_lang = NULL; + gchar *minimized_tag = g_new0(gchar, capacity); + int result = bcp47_minimize(lang_id, minimized_tag, capacity); + if (result < 0) { + g_strlcpy(minimized_tag, lang_id, capacity); + } + + gchar *lang_code = g_new0(gchar, capacity); + if (!bcp47_get_language_code(minimized_tag, lang_code, capacity)) { + g_strlcpy(lang_code, minimized_tag, capacity); + } + + // If ibus doesn't know about the language then append the + // language name to the keyboard name + if (lang_name != NULL) { + gchar *ibus_lang = ibus_get_untranslated_language_name(lang_code); + g_debug("%s: untranslated ibus language for %s: %s", __FUNCTION__, minimized_tag, ibus_lang); + if (g_strcmp0(ibus_lang, "Other") == 0) { + name_with_lang = g_strjoin(" - ", keyboard->name, lang_name, NULL); + } + g_free(ibus_lang); + } + + gchar *abs_kmx = g_strjoin("/", kmp_dir, keyboard->kmx_file, NULL); + gchar *id_with_lang = g_strjoin(":", minimized_tag, abs_kmx, NULL); + + g_message("adding engine %s", id_with_lang); + engine_desc = ibus_keyman_engine_desc_new( + id_with_lang, // lang:kmx full path + name_with_lang ? name_with_lang : keyboard->name, // longname + kbd_details->description, // description + info->copyright, // copyright if available + lang_code, // language, most are ignored by ibus except major languages + kbd_details->license, // license + info->author_desc, // author name only, not email + keyman_get_icon_file(abs_kmx), // icon full path + "us", // layout defaulting to us (en-US) + keyboard->version); + g_free(abs_kmx); + g_free(lang_code); + g_free(minimized_tag); + g_free(id_with_lang); + g_free(name_with_lang); + return engine_desc; +} + +// Add a keyboard (ibus engine) to the list of engines +void +keyman_add_keyboard(gpointer data, gpointer user_data) { + kmp_keyboard *keyboard = (kmp_keyboard *)data; + add_keyboard_data *kb_data = (add_keyboard_data *)user_data; + gboolean alreadyexists = FALSE; + + for (GList *e = kb_data->engines_list; e != NULL && alreadyexists == FALSE; e = e->next) { + IBusEngineDesc *engine_desc = (IBusEngineDesc *)e->data; + const gchar *version = ibus_engine_desc_get_version(engine_desc); + const gchar *engine_name = ibus_engine_desc_get_name(engine_desc); + gchar *kmx_file = g_path_get_basename(engine_name); + // If we already have an engine for this keyboard (in a different area), we + // don't want to add it again since we wouldn't add anything new + // if it's the same version + // TODO: fix version comparison (#9593) + if (g_strcmp0(kmx_file, keyboard->kmx_file) == 0 && g_strcmp0(version, keyboard->version) >= 0) { + alreadyexists = TRUE; + g_debug("keyboard %s already exists at version %s which is newer or same as %s", kmx_file, version, keyboard->version); + } + g_free(kmx_file); + } + + if (!alreadyexists) { + gchar *json_file = g_strjoin(".", keyboard->id, "json", NULL); + keyboard_details *kbd_details = g_new0(keyboard_details, 1); + get_keyboard_details(kb_data->kmp_dir, json_file, kbd_details); + g_free(json_file); + + if (keyboard->languages != NULL) { + for (GList *l = keyboard->languages; l != NULL; l = l->next) { + kmp_language *language = (kmp_language *)l->data; + IBusEngineDesc *engine_desc = + get_engine_for_language(keyboard, kb_data->info, kbd_details, kb_data->kmp_dir, language->id, language->name); + if (engine_desc) { + kb_data->engines_list = g_list_append(kb_data->engines_list, engine_desc); } - free_kmp_details(details); - g_free(details); + } + } else { + gchar *abs_kmx = g_strjoin("/", kb_data->kmp_dir, keyboard->kmx_file, NULL); + g_message("adding engine %s", abs_kmx); + kb_data->engines_list = g_list_append( + kb_data->engines_list, + ibus_keyman_engine_desc_new( + abs_kmx, // kmx full path + keyboard->name, // longname + kbd_details->description, // description + kb_data->info->copyright, // copyright if available + NULL, // language, most are ignored by ibus except major languages + kbd_details->license, // license + kb_data->info->author_desc, // author name only, not email + keyman_get_icon_file(abs_kmx), // icon full path + "us", // layout defaulting to us (en-US) + keyboard->version)); + g_free(abs_kmx); } - return engines; + free_keyboard_details(kbd_details); + g_free(kbd_details); + } +} + +// Add keyboards found in {kmp_dir}/kmp.json to engines_list +void +keyman_add_keyboards_from_dir(gpointer data, gpointer user_data) { + gchar * kmp_dir = (gchar *) data; + GList ** engines_list = (GList **)user_data; + + kmp_details *details = g_new0(kmp_details, 1); + if (get_kmp_details(kmp_dir, details) == JSON_OK) { + add_keyboard_data kb_data; + kb_data.engines_list = *engines_list; + kb_data.info = &details->info; + kb_data.kmp_dir = kmp_dir; + + g_list_foreach(details->keyboards, keyman_add_keyboard, &kb_data); + *engines_list = kb_data.engines_list; + } + free_kmp_details(details); + g_free(details); } GList * ibus_keyman_list_engines (void) { GList *engines = NULL; - GList *keyboard_list; + GList *kmpdir_list; gchar *local_keyboard_path, *xdgenv; g_debug("adding from /usr/share/keyman"); - keyboard_list = keyman_get_kmpdirs_fromdir(NULL, "/usr/share/keyman"); + kmpdir_list = keyman_get_kmpdirs_fromdir(NULL, "/usr/share/keyman"); g_debug("adding from /usr/local/share/keyman"); - keyboard_list = keyman_get_kmpdirs_fromdir(keyboard_list, "/usr/local/share/keyman"); + kmpdir_list = keyman_get_kmpdirs_fromdir(kmpdir_list, "/usr/local/share/keyman"); xdgenv = getenv("XDG_DATA_HOME"); if (xdgenv != NULL){ local_keyboard_path= g_strdup_printf("%s/keyman", xdgenv); @@ -282,10 +313,10 @@ ibus_keyman_list_engines (void) local_keyboard_path= g_strdup_printf("%s/.local/share/keyman", xdgenv); } g_debug("adding from %s", local_keyboard_path); - keyboard_list = keyman_get_kmpdirs_fromdir(keyboard_list, local_keyboard_path); + kmpdir_list = keyman_get_kmpdirs_fromdir(kmpdir_list, local_keyboard_path); g_free(local_keyboard_path); - engines = ibus_keyman_add_engines(engines, keyboard_list); - g_list_free(keyboard_list); + g_list_foreach(kmpdir_list, keyman_add_keyboards_from_dir, &engines); + g_list_free(kmpdir_list); return engines; } @@ -335,7 +366,7 @@ keyman_get_options_fromdconf(gchar *package_id, g_message("keyman_get_options_fromdconf"); // Obtain keyboard options from DConf - gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, package_id, keyboard_id); + g_autofree gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, package_id, keyboard_id); GSettings *child_settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); gchar **options = NULL; if (child_settings != NULL) @@ -344,7 +375,6 @@ keyman_get_options_fromdconf(gchar *package_id, } g_object_unref(G_OBJECT(child_settings)); - g_free(path); return options; } @@ -364,7 +394,7 @@ keyman_get_options_queue_fromdconf(gchar *package_id, GQueue *queue_options = g_queue_new(); // Obtain keyboard options from DConf - gchar **options = keyman_get_options_fromdconf(package_id, keyboard_id); + g_autofree gchar **options = keyman_get_options_fromdconf(package_id, keyboard_id); // Parse options into queue_options if (options != NULL) @@ -372,7 +402,7 @@ keyman_get_options_queue_fromdconf(gchar *package_id, int index = 0; while (options[index] != NULL) { - gchar **option_tokens = g_strsplit(options[index], "=", 2); + g_autofree gchar **option_tokens = g_strsplit(options[index], "=", 2); if (option_tokens != NULL && option_tokens[0] != NULL && option_tokens[1] != NULL) { g_message("Keyboard Option [%d], %s=%s", index, option_tokens[0], option_tokens[1]); @@ -386,7 +416,6 @@ keyman_get_options_queue_fromdconf(gchar *package_id, } index++; } - g_strfreev(options); } return queue_options; @@ -416,47 +445,37 @@ keyman_put_options_todconf(gchar *package_id, // Obtain keyboard options from DConf gchar **options = keyman_get_options_fromdconf(package_id, keyboard_id); - gchar *needle = g_strdup_printf("%s=", option_key); + g_autofree gchar *needle = g_strdup_printf("%s=", option_key); gchar *kvp = g_strdup_printf("%s=%s", option_key, option_value); - if (options != NULL) - { - int index = 0; - gboolean option_updated = FALSE; - while (options[index] != NULL) - { - // If option_key already exists, update value with option_value - if (g_strrstr(options[index], needle) != NULL) - { - g_free(options[index]); - options[index] = kvp; - option_updated = TRUE; - break; - } - index++; - } + g_assert(options != NULL); - if (!option_updated) + int index = 0; + gboolean option_updated = FALSE; + while (options[index] != NULL) + { + // If option_key already exists, update value with option_value + if (g_strrstr(options[index], needle) != NULL) { - // Resize to add new option and null-terminate - int size = index + 2; // old size: index + 1, plus 1 new - options = g_renew(gchar*, options, size); + g_free(options[index]); options[index] = kvp; - options[index+1] = NULL; + option_updated = TRUE; + break; } + index++; } - else + + if (!option_updated) { - // we never should come here - keyman_get_options_fromdconf will create empty - // options if they don't yet exist. - // Allocate space for new option and null-terminate - options = g_new(gchar *, 2); - options[0] = kvp; - options[1] = NULL; + // Resize to add new option and null-terminate + int size = index + 2; // old size: index + 1, plus 1 new + options = g_renew(gchar*, options, size); + options[index] = kvp; + options[index+1] = NULL; } // Write to DConf - gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, package_id, keyboard_id); + g_autofree gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, package_id, keyboard_id); GSettings *child_settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); if (child_settings != NULL) { @@ -465,8 +484,6 @@ keyman_put_options_todconf(gchar *package_id, } g_object_unref(G_OBJECT(child_settings)); - g_free(path); - g_free(needle); g_strfreev(options); // kvp got assigned to options[x] and so got freed by g_strfreev() } diff --git a/linux/ibus-keyman/src/keymanutil_internal.h b/linux/ibus-keyman/src/keymanutil_internal.h new file mode 100644 index 00000000000..112f7abd16e --- /dev/null +++ b/linux/ibus-keyman/src/keymanutil_internal.h @@ -0,0 +1,39 @@ +// Internal data structures used in the implementation of keymanutil methods +// and exposed for unit testing. + +#ifndef __KEYMANUTIL_INTERNAL_H__ +#define __KEYMANUTIL_INTERNAL_H__ + +#include +#include "kmpdetails.h" + +typedef struct { + GList *engines_list; + kmp_info *info; + gchar *kmp_dir; +} add_keyboard_data; + +IBusEngineDesc *ibus_keyman_engine_desc_new( + gchar *file_name, + gchar *name, + gchar *description, + gchar *copyright, + gchar *lang, + gchar *license, + gchar *author, + gchar *icon, + gchar *layout, + gchar *version); + +IBusEngineDesc *get_engine_for_language( + kmp_keyboard *keyboard, + kmp_info *info, + keyboard_details *kbd_details, + gchar *kmp_dir, + gchar *lang_id, + gchar *lang_name); + +void keyman_add_keyboard(gpointer data, gpointer user_data); +void keyman_add_keyboards_from_dir(gpointer data, gpointer user_data); + +#endif // __KEYMANUTIL_INTERNAL_H__ diff --git a/linux/ibus-keyman/src/test/keymanutil_tests.c b/linux/ibus-keyman/src/test/keymanutil_tests.c index 80f77a72ea6..924215f77d4 100644 --- a/linux/ibus-keyman/src/test/keymanutil_tests.c +++ b/linux/ibus-keyman/src/test/keymanutil_tests.c @@ -1,59 +1,139 @@ #include #include #include +#include +#include "kmpdetails.h" #include "keymanutil.h" +#include "keymanutil_internal.h" #define TEST_FIXTURE "keymanutil-test" void delete_options_key(gchar* testname) { - gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, TEST_FIXTURE, testname); - GSettings *settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); + g_autofree gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, TEST_FIXTURE, testname); + g_autoptr(GSettings) settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); g_settings_reset(settings, KEYMAN_DCONF_OPTIONS_KEY); - g_object_unref(G_OBJECT(settings)); - g_free(path); } void set_options_key(gchar* testname, gchar** options) { - gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, TEST_FIXTURE, testname); - GSettings *settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); + g_autofree gchar* path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, TEST_FIXTURE, testname); + g_autoptr(GSettings) settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); g_settings_set_strv(settings, KEYMAN_DCONF_OPTIONS_KEY, (const gchar* const*)options); - g_object_unref(G_OBJECT(settings)); - g_free(path); } gchar** get_options_key(gchar* testname) { - gchar* path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, TEST_FIXTURE, testname); - GSettings* settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); + g_autofree gchar* path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, TEST_FIXTURE, testname); + g_autoptr(GSettings) settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); gchar** result = g_settings_get_strv(settings, KEYMAN_DCONF_OPTIONS_KEY); - g_object_unref(G_OBJECT(settings)); - g_free(path); return result; } +kmp_keyboard* +_get_kmp_keyboard(gchar* version, gchar** languages) { + kmp_keyboard* keyboard = g_new0(kmp_keyboard, 1); + keyboard->name = g_strdup("Testing"); + keyboard->id = g_strdup("tst"); + keyboard->version = g_strdup(version); + keyboard->kmx_file = g_strdup("tst.kmx"); + keyboard->kvk_file = g_strdup(""); + keyboard->languages = NULL; + for (gchar* lang = *languages++; lang; lang = *languages++) { + gchar** tokens = g_strsplit(lang, ":", 2); + kmp_language* kmp_lang = g_new0(kmp_language, 1); + kmp_lang->id = g_strdup(tokens[0]); + kmp_lang->name = g_strdup(tokens[1]); + g_strfreev(tokens); + keyboard->languages = g_list_append(keyboard->languages, kmp_lang); + } + return keyboard; +} + +kmp_info* +_get_kmp_info(gchar * copyright, gchar * author_desc, gchar * author_url) { + kmp_info* info = g_new0(kmp_info, 1); + info->copyright = g_strdup(copyright); + info->author_desc = g_strdup(author_desc); + info->author_url = g_strdup(author_url); + return info; +} + +keyboard_details* +_get_keyboard_details(gchar * description, gchar * license) { + keyboard_details* details = g_new0(keyboard_details, 1); + details->id = g_strdup("tst"); + details->description = g_strdup(description); + details->license = g_strdup(license); + return details; +} + +add_keyboard_data* +_get_keyboard_data() { + add_keyboard_data* kb_data = g_new0(add_keyboard_data, 1); + kb_data->engines_list = NULL; + kb_data->info = _get_kmp_info(NULL, NULL, NULL); + kb_data->kmp_dir = "/tmp"; + return kb_data; +} + +void +free_kb_data(add_keyboard_data* kb_data) { + if (kb_data->engines_list) + g_list_free(kb_data->engines_list); + if (kb_data->info) + g_free(kb_data->info); + g_free(kb_data); +} + +// defined in kmpdetails +void free_keyboard(gpointer data); +void free_info(gpointer data); +kmp_json_status free_keyboard_details(keyboard_details* kbd_details); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(add_keyboard_data, free_kb_data) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(kmp_keyboard, free_keyboard) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(kmp_info, free_info) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(keyboard_details, free_keyboard_details) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(IBusEngineDesc, g_object_unref) + +//---------------------------------------------------------------------------------------------- +void +test_keyman_put_options_todconf__invalid() { + // Initialize + gchar* testname = "test_keyman_put_options_todconf__test_keyman_put_options_todconf__invalid"; + delete_options_key(testname); + + // Execute + keyman_put_options_todconf(TEST_FIXTURE, testname, "new_key", NULL); + + // Verify + g_auto(GStrv) options = get_options_key(testname); + g_assert_nonnull(options); + g_assert_null(options[0]); + + // Cleanup + delete_options_key(testname); +} + void test_keyman_put_options_todconf__new_key() { // Initialize gchar* testname = "test_keyman_put_options_todconf__new_key"; delete_options_key(testname); - gchar* value = g_strdup_printf("%d", g_test_rand_int()); + g_autofree gchar* value = g_strdup_printf("%d", g_test_rand_int()); // Execute keyman_put_options_todconf(TEST_FIXTURE, testname, "new_key", value); // Verify - gchar** options = get_options_key(testname); - gchar* expected = g_strdup_printf("new_key=%s", value); + g_auto(GStrv) options = get_options_key(testname); + g_autofree gchar* expected = g_strdup_printf("new_key=%s", value); g_assert_nonnull(options); g_assert_cmpstr(options[0], ==, expected); g_assert_null(options[1]); // Cleanup - g_free(expected); - g_free(value); - g_strfreev(options); delete_options_key(testname); } @@ -64,14 +144,14 @@ test_keyman_put_options_todconf__other_keys() { delete_options_key(testname); gchar* existingKeys[] = {"key1=val1", "key2=val2", NULL}; set_options_key(testname, existingKeys); - gchar* value = g_strdup_printf("%d", g_test_rand_int()); + g_autofree gchar* value = g_strdup_printf("%d", g_test_rand_int()); // Execute keyman_put_options_todconf(TEST_FIXTURE, testname, "new_key", value); // Verify - gchar** options = get_options_key(testname); - gchar* expected = g_strdup_printf("new_key=%s", value); + g_auto(GStrv) options = get_options_key(testname); + g_autofree gchar* expected = g_strdup_printf("new_key=%s", value); g_assert_nonnull(options); g_assert_cmpstr(options[0], ==, "key1=val1"); g_assert_cmpstr(options[1], ==, "key2=val2"); @@ -79,9 +159,6 @@ test_keyman_put_options_todconf__other_keys() { g_assert_null(options[3]); // Cleanup - g_free(expected); - g_free(value); - g_strfreev(options); delete_options_key(testname); } @@ -92,37 +169,537 @@ test_keyman_put_options_todconf__existing_key() { delete_options_key(testname); gchar* existingKeys[] = {"key1=val1", "new_key=val2", NULL}; set_options_key(testname, existingKeys); - gchar* value = g_strdup_printf("%d", g_test_rand_int()); + g_autofree gchar* value = g_strdup_printf("%d", g_test_rand_int()); // Execute keyman_put_options_todconf(TEST_FIXTURE, testname, "new_key", value); // Verify - gchar** options = get_options_key(testname); - gchar* expected = g_strdup_printf("new_key=%s", value); + g_auto(GStrv) options = get_options_key(testname); + g_autofree gchar* expected = g_strdup_printf("new_key=%s", value); g_assert_nonnull(options); g_assert_cmpstr(options[0], ==, "key1=val1"); g_assert_cmpstr(options[1], ==, expected); g_assert_null(options[2]); // Cleanup - g_free(expected); - g_free(value); - g_strfreev(options); delete_options_key(testname); } -int -main(int argc, char* argv[]) { +//---------------------------------------------------------------------------------------------- +void +test_ibus_keyman_engine_desc_new__all_set() { + // Execute + g_autoptr(IBusEngineDesc) desc = ibus_keyman_engine_desc_new("name", "longname", "description", "copyright", "lang", "license", "author", "icon", "layout", "version"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "name"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "longname"); + g_assert_cmpstr(ibus_engine_desc_get_description(desc), ==, "description\ncopyright"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "lang"); + g_assert_cmpstr(ibus_engine_desc_get_license(desc), ==, "license"); + g_assert_cmpstr(ibus_engine_desc_get_author(desc), ==, "author"); + g_assert_cmpstr(ibus_engine_desc_get_icon(desc), ==, "icon"); + g_assert_cmpstr(ibus_engine_desc_get_layout(desc), ==, "layout"); + g_assert_cmpstr(ibus_engine_desc_get_version(desc), ==, "version"); +} + +void +test_ibus_keyman_engine_desc_new__only_description() { + // Execute + g_autoptr(IBusEngineDesc) desc = ibus_keyman_engine_desc_new( + "name", "longname", "description", NULL, "lang", "license", "author", "icon", "layout", "version"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "name"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "longname"); + g_assert_cmpstr(ibus_engine_desc_get_description(desc), ==, "description\n(null)"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "lang"); + g_assert_cmpstr(ibus_engine_desc_get_license(desc), ==, "license"); + g_assert_cmpstr(ibus_engine_desc_get_author(desc), ==, "author"); + g_assert_cmpstr(ibus_engine_desc_get_icon(desc), ==, "icon"); + g_assert_cmpstr(ibus_engine_desc_get_layout(desc), ==, "layout"); + g_assert_cmpstr(ibus_engine_desc_get_version(desc), ==, "version"); +} + +void +test_ibus_keyman_engine_desc_new__only_copyright() { + // Execute + g_autoptr(IBusEngineDesc) desc = ibus_keyman_engine_desc_new( + "name", "longname", NULL, "copyright", "lang", "license", "author", "icon", "layout", "version"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "name"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "longname"); + g_assert_cmpstr(ibus_engine_desc_get_description(desc), ==, "copyright"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "lang"); + g_assert_cmpstr(ibus_engine_desc_get_license(desc), ==, "license"); + g_assert_cmpstr(ibus_engine_desc_get_author(desc), ==, "author"); + g_assert_cmpstr(ibus_engine_desc_get_icon(desc), ==, "icon"); + g_assert_cmpstr(ibus_engine_desc_get_layout(desc), ==, "layout"); + g_assert_cmpstr(ibus_engine_desc_get_version(desc), ==, "version"); +} + +void +test_ibus_keyman_engine_desc_new__no_language_license_author_version() { + // Execute + g_autoptr(IBusEngineDesc) desc = ibus_keyman_engine_desc_new( + "name", "longname", "description", "copyright", NULL, NULL, NULL, "icon", "layout", NULL); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "name"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "longname"); + g_assert_cmpstr(ibus_engine_desc_get_description(desc), ==, "description\ncopyright"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "other"); + g_assert_cmpstr(ibus_engine_desc_get_license(desc), ==, ""); + g_assert_cmpstr(ibus_engine_desc_get_author(desc), ==, ""); + g_assert_cmpstr(ibus_engine_desc_get_icon(desc), ==, "icon"); + g_assert_cmpstr(ibus_engine_desc_get_layout(desc), ==, "layout"); + g_assert_cmpstr(ibus_engine_desc_get_version(desc), ==, ""); +} + +//---------------------------------------------------------------------------------------------- +void +test_get_engine_for_language__null_language() { + // Initialize + gchar* languages[] = {"en:English", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_kmp_keyboard("1.0", languages); + g_autoptr(kmp_info) info = _get_kmp_info("Copyright by me", "My Author", "myauthor@example.com"); + g_autoptr(keyboard_details) details = _get_keyboard_details("my description", "MIT"); + + // Execute + g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", NULL, NULL); + + // Verify + g_assert_null(desc); +} + +void +test_get_engine_for_language__empty_language() { + // Initialize + gchar* languages[] = {"en:English", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_kmp_keyboard("1.0", languages); + g_autoptr(kmp_info) info = _get_kmp_info("Copyright by me", "My Author", "myauthor@example.com"); + g_autoptr(keyboard_details) details = _get_keyboard_details("my description", "MIT"); + + // Execute + g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "", ""); + + // Verify + g_assert_null(desc); +} + +void +test_get_engine_for_language__one_language() { + // Initialize + gchar* languages[] = {"en:English", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_kmp_keyboard("1.0", languages); + g_autoptr(kmp_info) info = _get_kmp_info("Copyright by me", "My Author", "myauthor@example.com"); + g_autoptr(keyboard_details) details = _get_keyboard_details("my description", "MIT"); + + // Execute + g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "en", "English"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "en:/tmp/tst.kmx"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "Testing"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "en"); +} + +void +test_get_engine_for_language__one_unknown_language() { + // Initialize + gchar* languages[] = {"foo:Foo", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_kmp_keyboard("1.0", languages); + g_autoptr(kmp_info) info = _get_kmp_info("Copyright by me", "My Author", "myauthor@example.com"); + g_autoptr(keyboard_details) details = _get_keyboard_details("my description", "MIT"); + + // Execute + g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "foo", "Foo"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "foo:/tmp/tst.kmx"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "Testing - Foo"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "foo"); +} + +void +test_get_engine_for_language__different_languages() { + // Initialize + gchar* languages[] = {"en:English", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_kmp_keyboard("1.0", languages); + g_autoptr(kmp_info) info = _get_kmp_info("Copyright by me", "My Author", "myauthor@example.com"); + g_autoptr(keyboard_details) details = _get_keyboard_details("my description", "MIT"); + + // Execute + g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "foo", "Foo"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "foo:/tmp/tst.kmx"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "Testing - Foo"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "foo"); +} + +void +test_get_engine_for_language__no_kbd_language() { + // Initialize + gchar* languages[] = {NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_kmp_keyboard("1.0", languages); + g_autoptr(kmp_info) info = _get_kmp_info("Copyright by me", "My Author", "myauthor@example.com"); + g_autoptr(keyboard_details) details = _get_keyboard_details("my description", "MIT"); + + // Execute + g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "en", "English"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "en:/tmp/tst.kmx"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "Testing"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "en"); +} + +//---------------------------------------------------------------------------------------------- +void +test_keyman_add_keyboard__no_language() { + // Initialize + gchar* languages[] = {NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_kmp_keyboard("1.0", languages); + g_autoptr(add_keyboard_data) kb_data = _get_keyboard_data(); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + g_assert_nonnull(kb_data->engines_list->data); + IBusEngineDesc* desc = (IBusEngineDesc*)kb_data->engines_list->data; + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "/tmp/tst.kmx"); + g_assert_null(kb_data->engines_list->next); +} + +void +test_keyman_add_keyboard__one_language() { + // Initialize + gchar* languages[] = {"en:English", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_kmp_keyboard("1.0", languages); + g_autoptr(add_keyboard_data) kb_data = _get_keyboard_data(); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + g_assert_nonnull(kb_data->engines_list->data); + IBusEngineDesc* desc = (IBusEngineDesc*)kb_data->engines_list->data; + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "en:/tmp/tst.kmx"); + g_assert_null(kb_data->engines_list->next); +} + +void +test_keyman_add_keyboard__two_languages() { + // Initialize + gchar* languages[] = {"en:English", "fr:French", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_kmp_keyboard("1.0", languages); + g_autoptr(add_keyboard_data) kb_data = _get_keyboard_data(); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + g_assert_nonnull(kb_data->engines_list->data); + IBusEngineDesc* desc = (IBusEngineDesc*)kb_data->engines_list->data; + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "en:/tmp/tst.kmx"); + g_assert_nonnull(kb_data->engines_list->next); + g_assert_nonnull(kb_data->engines_list->next->data); + desc = (IBusEngineDesc*)kb_data->engines_list->next->data; + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "fr:/tmp/tst.kmx"); + g_assert_null(kb_data->engines_list->next->next); +} + +void +test_keyman_add_keyboard__prev_engine_adding_same_version() { + // Initialize + gchar* languages[] = {"en:English", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_kmp_keyboard("1.0", languages); + g_autoptr(add_keyboard_data) kb_data = _get_keyboard_data(); + IBusEngineDesc* desc = ibus_keyman_engine_desc_new("en:/usr/share/keyman/tst.kmx", "Testing", NULL, NULL, "en", NULL, NULL, "", "us", "1.0"); + kb_data->engines_list = g_list_append(kb_data->engines_list, desc); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + GList* list = kb_data->engines_list; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "en:/usr/share/keyman/tst.kmx"); + g_assert_null(list->next); +} + +void +test_keyman_add_keyboard__prev_engine_adding_newer_version() { + // Initialize + gchar* languages[] = {"en:English", "fr:French", NULL}; // New version adds French + g_autoptr(kmp_keyboard) keyboard = _get_kmp_keyboard("1.1", languages); + g_autoptr(add_keyboard_data) kb_data = _get_keyboard_data(); + IBusEngineDesc* desc = + ibus_keyman_engine_desc_new("en:/usr/share/keyman/tst.kmx", "Testing", NULL, NULL, "en", NULL, NULL, "", "us", "1.0"); + kb_data->engines_list = g_list_append(kb_data->engines_list, desc); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + GList* list = kb_data->engines_list; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "en:/usr/share/keyman/tst.kmx"); + g_assert_nonnull(list->next); + list = list->next; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "en:/tmp/tst.kmx"); + g_assert_nonnull(list->next); + list = list->next; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "fr:/tmp/tst.kmx"); + g_assert_null(list->next); +} + +void +test_keyman_add_keyboard__prev_engine_adding_newer_version_9593() { + // This tests bug #9593: We add keyboard version 1.10 while 1.9 is already in + // the list. + + // Initialize + gchar* languages[] = {"en:English", "fr:French", NULL}; // New version adds French + g_autoptr(kmp_keyboard) keyboard = _get_kmp_keyboard("1.10", languages); + g_autoptr(add_keyboard_data) kb_data = _get_keyboard_data(); + IBusEngineDesc* desc = + ibus_keyman_engine_desc_new("en:/usr/share/keyman/tst.kmx", "Testing", NULL, NULL, "en", NULL, NULL, "", "us", "1.9"); + kb_data->engines_list = g_list_append(kb_data->engines_list, desc); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + GList* list = kb_data->engines_list; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "en:/usr/share/keyman/tst.kmx"); + g_assert_nonnull(list->next); + list = list->next; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "en:/tmp/tst.kmx"); + g_assert_nonnull(list->next); + list = list->next; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "fr:/tmp/tst.kmx"); + g_assert_null(list->next); +} + +void +test_keyman_add_keyboard__prev_engine_adding_older_version() { + // Initialize + gchar* languages[] = {"en:English", "fr:French", NULL}; // Old version has additional French + g_autoptr(kmp_keyboard) keyboard = _get_kmp_keyboard("0.9", languages); + g_autoptr(add_keyboard_data) kb_data = _get_keyboard_data(); + IBusEngineDesc* desc = + ibus_keyman_engine_desc_new("en:/usr/share/keyman/tst.kmx", "Testing", NULL, NULL, "en", NULL, NULL, "", "us", "1.0"); + kb_data->engines_list = g_list_append(kb_data->engines_list, desc); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + GList* list = kb_data->engines_list; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "en:/usr/share/keyman/tst.kmx"); + g_assert_null(list->next); +} + +//---------------------------------------------------------------------------------------------- +gchar* testdata_dir; + +gboolean +delete_directory(gchar* path) { + g_autoptr(GFile) file = g_file_new_for_path(path); + if (g_file_test(path, G_FILE_TEST_IS_DIR)) { + g_autoptr(GFileEnumerator) enumerator = NULL; + + enumerator = g_file_enumerate_children(file, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL); + + while (enumerator != NULL) { + GFile* child; + + if (!g_file_enumerator_iterate(enumerator, NULL, &child, NULL, NULL)) + return FALSE; + if (child == NULL) + break; + if (!delete_directory(g_file_get_path(child))) + return FALSE; + } + } + + return g_file_delete(file, NULL, NULL); +} + +void clear_kmpdir(gchar* dir) { + delete_directory(dir); + g_free(dir); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(gchar, clear_kmpdir); + +void copy_test_data(gchar* kmp_dir, gchar* name) { + g_autofree gchar* oldfile = g_strdup_printf("%s/%s", testdata_dir, name); + g_autoptr(GFile) source = g_file_new_for_path(oldfile); + g_autofree gchar* newfile = g_strdup_printf("%s/kmp.json", kmp_dir); + g_autoptr(GFile) dest = g_file_new_for_path(newfile); + g_file_copy(source, dest, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, NULL); +} + +void +test_keyman_add_keyboards_from_dir__no_kmpjson() { + // Initialize + g_autolist(GList) keyboards = NULL; + g_autoptr(gchar) kmp_dir = g_dir_make_tmp(NULL, NULL); + + // Execute + keyman_add_keyboards_from_dir(kmp_dir, &keyboards); + + // Verify + g_assert_cmpint(g_list_length(keyboards), ==, 0); +} + +void +test_keyman_add_keyboards_from_dir__one_dir() { + // Initialize + g_autoptr(GList) keyboards = NULL; + g_autoptr(gchar) kmp_dir = g_dir_make_tmp(NULL, NULL); + copy_test_data(kmp_dir, "kmp1.json"); + + // Execute + keyman_add_keyboards_from_dir(kmp_dir, &keyboards); + + // Verify + g_assert_cmpint(g_list_length(keyboards), ==, 1); + + IBusEngineDesc* desc = IBUS_ENGINE_DESC(keyboards->data); + g_autofree gchar* expected_name = g_strdup_printf("bza:%s/test1.kmx", kmp_dir); + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name); +} + +void +test_keyman_add_keyboards_from_dir__two_langs() { + // Initialize + g_autoptr(GList) keyboards = NULL; + g_autoptr(gchar) kmp_dir = g_dir_make_tmp(NULL, NULL); + copy_test_data(kmp_dir, "kmp2.json"); + + // Execute + keyman_add_keyboards_from_dir(kmp_dir, &keyboards); + + // Verify + g_assert_cmpint(g_list_length(keyboards), ==, 2); + + IBusEngineDesc* desc = IBUS_ENGINE_DESC(keyboards->data); + g_autofree gchar* expected_name1 = g_strdup_printf("bmf-Latn:%s/test2.kmx", kmp_dir); + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name1); + desc = IBUS_ENGINE_DESC(keyboards->next->data); + g_autofree gchar* expected_name2 = g_strdup_printf("bun-Latn:%s/test2.kmx", kmp_dir); + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name2); +} + +void +test_keyman_add_keyboards_from_dir__prev_keyboards() { + // Initialize + g_autoptr(GList) keyboards = NULL; + g_autoptr(gchar) kmp_dir = g_dir_make_tmp(NULL, NULL); + copy_test_data(kmp_dir, "kmp2.json"); + keyman_add_keyboards_from_dir(kmp_dir, &keyboards); + copy_test_data(kmp_dir, "kmp1.json"); + + // Execute + keyman_add_keyboards_from_dir(kmp_dir, &keyboards); + + // Verify + g_assert_cmpint(g_list_length(keyboards), ==, 3); + + IBusEngineDesc* desc = IBUS_ENGINE_DESC(keyboards->data); + g_autofree gchar* expected_name1 = g_strdup_printf("bmf-Latn:%s/test2.kmx", kmp_dir); + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name1); + desc = IBUS_ENGINE_DESC(keyboards->next->data); + g_autofree gchar* expected_name2 = g_strdup_printf("bun-Latn:%s/test2.kmx", kmp_dir); + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name2); + desc = IBUS_ENGINE_DESC(keyboards->next->next->data); + g_autofree gchar* expected_name = g_strdup_printf("bza:%s/test1.kmx", kmp_dir); + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name); +} + +//---------------------------------------------------------------------------------------------- +void +print_usage() { + printf( + "Usage: %s --testdata \n\n", + g_get_prgname()); + printf("Arguments:\n"); + printf("\t--testdata \tThe directory containing test kmp.json files for the tests.\n\n"); +} + +int main(int argc, char* argv[]) { gtk_init(&argc, &argv); g_test_init(&argc, &argv, NULL); g_test_set_nonfatal_assertions(); + if (argc < 3 || strcmp(argv[1], "--testdata") != 0) { + print_usage(); + return 1; + } + + testdata_dir = argv[2]; + + if (!g_file_test(testdata_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { + printf("ERROR: Testdata directory %s does not exist\n\n", testdata_dir); + print_usage(); + return 2; + } + // Add tests + g_test_add_func("/keymanutil/keyman_put_options_todconf/invalid", test_keyman_put_options_todconf__invalid); g_test_add_func("/keymanutil/keyman_put_options_todconf/new_key", test_keyman_put_options_todconf__new_key); g_test_add_func("/keymanutil/keyman_put_options_todconf/other_keys", test_keyman_put_options_todconf__other_keys); g_test_add_func("/keymanutil/keyman_put_options_todconf/existing_key", test_keyman_put_options_todconf__existing_key); + g_test_add_func("/keymanutil/ibus_keyman_engine_desc_new/all_set", test_ibus_keyman_engine_desc_new__all_set); + g_test_add_func("/keymanutil/ibus_keyman_engine_desc_new/only_description", test_ibus_keyman_engine_desc_new__only_description); + g_test_add_func("/keymanutil/ibus_keyman_engine_desc_new/only_copyright", test_ibus_keyman_engine_desc_new__only_copyright); + g_test_add_func( + "/keymanutil/ibus_keyman_engine_desc_new/no_language_license_author_version", + test_ibus_keyman_engine_desc_new__no_language_license_author_version); + + g_test_add_func("/keymanutil/get_engine_for_language/null_language", test_get_engine_for_language__null_language); + g_test_add_func("/keymanutil/get_engine_for_language/empty_language", test_get_engine_for_language__empty_language); + g_test_add_func("/keymanutil/get_engine_for_language/one_language", test_get_engine_for_language__one_language); + g_test_add_func("/keymanutil/get_engine_for_language/one_unknown_language", test_get_engine_for_language__one_unknown_language); + g_test_add_func("/keymanutil/get_engine_for_language/different_languages", test_get_engine_for_language__different_languages); + g_test_add_func("/keymanutil/get_engine_for_language/no_kbd_language", test_get_engine_for_language__no_kbd_language); + + g_test_add_func("/keymanutil/keyman_add_keyboard/no_language", test_keyman_add_keyboard__no_language); + g_test_add_func("/keymanutil/keyman_add_keyboard/one_language", test_keyman_add_keyboard__one_language); + g_test_add_func("/keymanutil/keyman_add_keyboard/two_languages", test_keyman_add_keyboard__two_languages); + g_test_add_func( + "/keymanutil/keyman_add_keyboard/prev_engine_adding_same_version", + test_keyman_add_keyboard__prev_engine_adding_same_version); + g_test_add_func( + "/keymanutil/keyman_add_keyboard/prev_engine_adding_newer_version", + test_keyman_add_keyboard__prev_engine_adding_newer_version); + // #9593 + // g_test_add_func( + // "/keymanutil/keyman_add_keyboard/prev_engine_adding_newer_version_versioncompare", + // test_keyman_add_keyboard__prev_engine_adding_newer_version_9593); + g_test_add_func( + "/keymanutil/keyman_add_keyboard/prev_engine_adding_older_version", + test_keyman_add_keyboard__prev_engine_adding_older_version); + + g_test_add_func("/keymanutil/keyman_add_keyboards_from_dir/no_kmpjson", test_keyman_add_keyboards_from_dir__no_kmpjson); + g_test_add_func("/keymanutil/keyman_add_keyboards_from_dir/one_dir", test_keyman_add_keyboards_from_dir__one_dir); + g_test_add_func("/keymanutil/keyman_add_keyboards_from_dir/two_langs", test_keyman_add_keyboards_from_dir__two_langs); + g_test_add_func("/keymanutil/keyman_add_keyboards_from_dir/prev_keyboards", test_keyman_add_keyboards_from_dir__prev_keyboards); + // Run tests int retVal = g_test_run(); diff --git a/linux/ibus-keyman/src/test/meson.build b/linux/ibus-keyman/src/test/meson.build index 3c8c1044c0d..02631e27f79 100644 --- a/linux/ibus-keyman/src/test/meson.build +++ b/linux/ibus-keyman/src/test/meson.build @@ -84,7 +84,7 @@ test( test( 'keymanutil-tests', run_src_test, - args: [ '--tap', '-k', '--env', env_file, '--', keymanutil_tests], + args: [ '--tap', '-k', '--env', env_file, '--', keymanutil_tests, '--testdata', meson.current_source_dir() / 'testdata'], env: test_env, priority: -2, is_parallel: false, @@ -103,7 +103,7 @@ test( test( 'print-kmp-test', run_src_test, - args: [ '--', print_kmp_test, meson.current_source_dir() / 'kmp.json' ], + args: [ '--', print_kmp_test, meson.current_source_dir() / 'testdata/kmp.json' ], env: test_env, priority: -2, protocol: 'exitcode', diff --git a/linux/ibus-keyman/src/test/run-tests.sh b/linux/ibus-keyman/src/test/run-tests.sh index efeb747ddc7..ae005fcbd6b 100755 --- a/linux/ibus-keyman/src/test/run-tests.sh +++ b/linux/ibus-keyman/src/test/run-tests.sh @@ -58,4 +58,4 @@ glib-compile-schemas "$SCHEMA_DIR" export GSETTINGS_BACKEND=memory -"${G_TEST_BUILDDIR:-.}/keymanutil-tests" "$@" +"${G_TEST_BUILDDIR:-.}/keymanutil-tests" "$@" --testdata "${SRCDIR}/src/test/testdata" diff --git a/linux/ibus-keyman/src/test/setup-tests.sh b/linux/ibus-keyman/src/test/setup-tests.sh index 07e7d5462b3..79bb765f509 100755 --- a/linux/ibus-keyman/src/test/setup-tests.sh +++ b/linux/ibus-keyman/src/test/setup-tests.sh @@ -3,4 +3,4 @@ set -eu . "$(dirname "$0")/../../tests/scripts/test-helper.inc.sh" -setup_display_server_only "$1" "$2" "$3" "$4" +setup_display_server_only "${1:-x11}" "${2:-/tmp/env-src-test.txt}" "${3:-/tmp/ibus-keyman-src-test-pids}" "${4:-/tmp/ibus-keyman-src-test-pids.pids}" diff --git a/linux/ibus-keyman/src/test/kmp.json b/linux/ibus-keyman/src/test/testdata/kmp.json similarity index 95% rename from linux/ibus-keyman/src/test/kmp.json rename to linux/ibus-keyman/src/test/testdata/kmp.json index dc659d324e7..d2ee4fa9e13 100644 --- a/linux/ibus-keyman/src/test/kmp.json +++ b/linux/ibus-keyman/src/test/testdata/kmp.json @@ -1,267 +1,267 @@ -{ - "system": { - "keymanDeveloperVersion": "10.0.1202.0", - "fileVersion": "7.0" - }, - "options": { - "readmeFile": "readme.htm" - }, - "info": { - "version": { - "description": "1.6.1" - }, - "name": { - "description": "LIBTRALO" - }, - "copyright": { - "description": "\u00A9 2008-2018 SIL International" - }, - "author": { - "description": "support@keyman.com", - "url": "mailto:support@keyman.com" - } - }, - "files": [ - { - "name": "libtralo.kmx", - "description": "Keyboard LIBTRALO" - }, - { - "name": "readme.htm", - "description": "File readme.htm" - }, - { - "name": "libtralo.png", - "description": "File libtralo.png" - }, - { - "name": "welcome.htm", - "description": "File welcome.htm" - }, - { - "name": "Charis_SIL_README.txt", - "description": "File Charis_SIL_README.txt" - }, - { - "name": "CharisSIL-B.ttf", - "description": "Font Charis SIL Bold" - }, - { - "name": "CharisSIL-BI.ttf", - "description": "Font Charis SIL Bold Italic" - }, - { - "name": "CharisSIL-I.ttf", - "description": "Font Charis SIL Italic" - }, - { - "name": "CharisSIL-R.ttf", - "description": "Font Charis SIL" - }, - { - "name": "FONTLOG.txt", - "description": "File FONTLOG.txt" - }, - { - "name": "OFL.txt", - "description": "File OFL.txt" - }, - { - "name": "OFL-FAQ.txt", - "description": "File OFL-FAQ.txt" - }, - { - "name": "CharisSILDan-Bold.ttf", - "description": "Font Charis SIL Dan Bold" - }, - { - "name": "CharisSILDan-BoldItalic.ttf", - "description": "Font Charis SIL Dan Bold Italic" - }, - { - "name": "CharisSILDan-Italic.ttf", - "description": "Font Charis SIL Dan Italic" - }, - { - "name": "CharisSILDan-Regular.ttf", - "description": "Font Charis SIL Dan" - }, - { - "name": "libtralo.js", - "description": "File libtralo.js" - }, - { - "name": "libtralo.kvk", - "description": "File libtralo.kvk" - }, - { - "name": "CharisSILDan_FONTLOG.txt", - "description": "File CharisSILDan_FONTLOG.txt" - }, - { - "name": "kmp.inf", - "description": "Package information" - }, - { - "name": "kmp.json", - "description": "Package information (JSON)" - } - ], - "keyboards": [ - { - "name": "LIBTRALO", - "id": "libtralo", - "version": "1.6.1", - "languages": [ - { - "name": "Bandi", - "id": "bza-Latn" - }, - { - "name": "Bassa", - "id": "bsq-Latn" - }, - { - "name": "Glio-Oubi", - "id": "oub-Latn" - }, - { - "name": "Gola", - "id": "gol-Latn" - }, - { - "name": "Northern Grebo", - "id": "gbo-Latn" - }, - { - "name": "Southern Kisi", - "id": "kss-Latn" - }, - { - "name": "Klao", - "id": "klu-Latn" - }, - { - "name": "Liberia Kpelle (Latin)", - "id": "xpe-Latn" - }, - { - "name": "Eastern Krahn", - "id": "kqo-Latn" - }, - { - "name": "Western Krahn", - "id": "krw-Latn" - }, - { - "name": "Kuwaa", - "id": "blh-Latn" - }, - { - "name": "Loma (Liberia) (Latin)", - "id": "lom-Latn" - }, - { - "name": "Mano", - "id": "mev-Latn" - }, - { - "name": "Manya", - "id": "mzj-Latn" - }, - { - "name": "Sapo", - "id": "krn-Latn" - }, - { - "name": "Bullom So", - "id": "buy-Latn" - }, - { - "name": "Kono (Sierra Leone)", - "id": "kno-Latn" - }, - { - "name": "Krio", - "id": "kri-Latn" - }, - { - "name": "Kuranko", - "id": "knk-Latn" - }, - { - "name": "West-Central Limba", - "id": "lia-Latn" - }, - { - "name": "Loko", - "id": "lok-Latn" - }, - { - "name": "Mende (Sierra Leone)", - "id": "men" - }, - { - "name": "Timne", - "id": "tem" - }, - { - "name": "Vai (Latin)", - "id": "vai-Latn" - }, - { - "name": "Dewoin (Latin)", - "id": "dee-Latn" - }, - { - "name": "Dan (Western) (Latin)", - "id": "dnj-Latn-LR" - }, - { - "name": "Gbii (Latin)", - "id": "ggb-Latn" - }, - { - "name": "Glaro-Twabo (Latin)", - "id": "glr-Latn" - }, - { - "name": "Grebo (Latin)", - "id": "grb-Latn" - }, - { - "name": "Barclayville Grebo (Latin)", - "id": "gry-Latn" - }, - { - "name": "Central Grebo (Latin)", - "id": "grv-Latn" - }, - { - "name": "Gboloo Grebo (Latin)", - "id": "gec-Latn" - }, - { - "name": "Southern Grebo (Latin)", - "id": "grj-Latn" - }, - { - "name": "Kpelle (Latin)", - "id": "kpe-Latn" - }, - { - "name": "Tajuasohn (Latin)", - "id": "tja-Latn" - }, - { - "name": "Bom-Kim (Latin)", - "id": "bmf-Latn" - }, - { - "name": "Sherbro (Latin)", - "id": "bun-Latn" - } - ] - } - ] -} +{ + "system": { + "keymanDeveloperVersion": "10.0.1202.0", + "fileVersion": "7.0" + }, + "options": { + "readmeFile": "readme.htm" + }, + "info": { + "version": { + "description": "1.6.1" + }, + "name": { + "description": "LIBTRALO" + }, + "copyright": { + "description": "\u00A9 2008-2018 SIL International" + }, + "author": { + "description": "support@keyman.com", + "url": "mailto:support@keyman.com" + } + }, + "files": [ + { + "name": "libtralo.kmx", + "description": "Keyboard LIBTRALO" + }, + { + "name": "readme.htm", + "description": "File readme.htm" + }, + { + "name": "libtralo.png", + "description": "File libtralo.png" + }, + { + "name": "welcome.htm", + "description": "File welcome.htm" + }, + { + "name": "Charis_SIL_README.txt", + "description": "File Charis_SIL_README.txt" + }, + { + "name": "CharisSIL-B.ttf", + "description": "Font Charis SIL Bold" + }, + { + "name": "CharisSIL-BI.ttf", + "description": "Font Charis SIL Bold Italic" + }, + { + "name": "CharisSIL-I.ttf", + "description": "Font Charis SIL Italic" + }, + { + "name": "CharisSIL-R.ttf", + "description": "Font Charis SIL" + }, + { + "name": "FONTLOG.txt", + "description": "File FONTLOG.txt" + }, + { + "name": "OFL.txt", + "description": "File OFL.txt" + }, + { + "name": "OFL-FAQ.txt", + "description": "File OFL-FAQ.txt" + }, + { + "name": "CharisSILDan-Bold.ttf", + "description": "Font Charis SIL Dan Bold" + }, + { + "name": "CharisSILDan-BoldItalic.ttf", + "description": "Font Charis SIL Dan Bold Italic" + }, + { + "name": "CharisSILDan-Italic.ttf", + "description": "Font Charis SIL Dan Italic" + }, + { + "name": "CharisSILDan-Regular.ttf", + "description": "Font Charis SIL Dan" + }, + { + "name": "libtralo.js", + "description": "File libtralo.js" + }, + { + "name": "libtralo.kvk", + "description": "File libtralo.kvk" + }, + { + "name": "CharisSILDan_FONTLOG.txt", + "description": "File CharisSILDan_FONTLOG.txt" + }, + { + "name": "kmp.inf", + "description": "Package information" + }, + { + "name": "kmp.json", + "description": "Package information (JSON)" + } + ], + "keyboards": [ + { + "name": "LIBTRALO", + "id": "libtralo", + "version": "1.6.1", + "languages": [ + { + "name": "Bandi", + "id": "bza-Latn" + }, + { + "name": "Bassa", + "id": "bsq-Latn" + }, + { + "name": "Glio-Oubi", + "id": "oub-Latn" + }, + { + "name": "Gola", + "id": "gol-Latn" + }, + { + "name": "Northern Grebo", + "id": "gbo-Latn" + }, + { + "name": "Southern Kisi", + "id": "kss-Latn" + }, + { + "name": "Klao", + "id": "klu-Latn" + }, + { + "name": "Liberia Kpelle (Latin)", + "id": "xpe-Latn" + }, + { + "name": "Eastern Krahn", + "id": "kqo-Latn" + }, + { + "name": "Western Krahn", + "id": "krw-Latn" + }, + { + "name": "Kuwaa", + "id": "blh-Latn" + }, + { + "name": "Loma (Liberia) (Latin)", + "id": "lom-Latn" + }, + { + "name": "Mano", + "id": "mev-Latn" + }, + { + "name": "Manya", + "id": "mzj-Latn" + }, + { + "name": "Sapo", + "id": "krn-Latn" + }, + { + "name": "Bullom So", + "id": "buy-Latn" + }, + { + "name": "Kono (Sierra Leone)", + "id": "kno-Latn" + }, + { + "name": "Krio", + "id": "kri-Latn" + }, + { + "name": "Kuranko", + "id": "knk-Latn" + }, + { + "name": "West-Central Limba", + "id": "lia-Latn" + }, + { + "name": "Loko", + "id": "lok-Latn" + }, + { + "name": "Mende (Sierra Leone)", + "id": "men" + }, + { + "name": "Timne", + "id": "tem" + }, + { + "name": "Vai (Latin)", + "id": "vai-Latn" + }, + { + "name": "Dewoin (Latin)", + "id": "dee-Latn" + }, + { + "name": "Dan (Western) (Latin)", + "id": "dnj-Latn-LR" + }, + { + "name": "Gbii (Latin)", + "id": "ggb-Latn" + }, + { + "name": "Glaro-Twabo (Latin)", + "id": "glr-Latn" + }, + { + "name": "Grebo (Latin)", + "id": "grb-Latn" + }, + { + "name": "Barclayville Grebo (Latin)", + "id": "gry-Latn" + }, + { + "name": "Central Grebo (Latin)", + "id": "grv-Latn" + }, + { + "name": "Gboloo Grebo (Latin)", + "id": "gec-Latn" + }, + { + "name": "Southern Grebo (Latin)", + "id": "grj-Latn" + }, + { + "name": "Kpelle (Latin)", + "id": "kpe-Latn" + }, + { + "name": "Tajuasohn (Latin)", + "id": "tja-Latn" + }, + { + "name": "Bom-Kim (Latin)", + "id": "bmf-Latn" + }, + { + "name": "Sherbro (Latin)", + "id": "bun-Latn" + } + ] + } + ] +} diff --git a/linux/ibus-keyman/src/test/testdata/kmp1.json b/linux/ibus-keyman/src/test/testdata/kmp1.json new file mode 100644 index 00000000000..5d9954c330e --- /dev/null +++ b/linux/ibus-keyman/src/test/testdata/kmp1.json @@ -0,0 +1,123 @@ +{ + "system": { + "keymanDeveloperVersion": "10.0.1202.0", + "fileVersion": "7.0" + }, + "options": { + "readmeFile": "readme.htm" + }, + "info": { + "version": { + "description": "1.6.1" + }, + "name": { + "description": "LIBTRALO" + }, + "copyright": { + "description": "\u00A9 2008-2018 SIL International" + }, + "author": { + "description": "support@keyman.com", + "url": "mailto:support@keyman.com" + } + }, + "files": [ + { + "name": "libtralo.kmx", + "description": "Keyboard LIBTRALO" + }, + { + "name": "readme.htm", + "description": "File readme.htm" + }, + { + "name": "libtralo.png", + "description": "File libtralo.png" + }, + { + "name": "welcome.htm", + "description": "File welcome.htm" + }, + { + "name": "Charis_SIL_README.txt", + "description": "File Charis_SIL_README.txt" + }, + { + "name": "CharisSIL-B.ttf", + "description": "Font Charis SIL Bold" + }, + { + "name": "CharisSIL-BI.ttf", + "description": "Font Charis SIL Bold Italic" + }, + { + "name": "CharisSIL-I.ttf", + "description": "Font Charis SIL Italic" + }, + { + "name": "CharisSIL-R.ttf", + "description": "Font Charis SIL" + }, + { + "name": "FONTLOG.txt", + "description": "File FONTLOG.txt" + }, + { + "name": "OFL.txt", + "description": "File OFL.txt" + }, + { + "name": "OFL-FAQ.txt", + "description": "File OFL-FAQ.txt" + }, + { + "name": "CharisSILDan-Bold.ttf", + "description": "Font Charis SIL Dan Bold" + }, + { + "name": "CharisSILDan-BoldItalic.ttf", + "description": "Font Charis SIL Dan Bold Italic" + }, + { + "name": "CharisSILDan-Italic.ttf", + "description": "Font Charis SIL Dan Italic" + }, + { + "name": "CharisSILDan-Regular.ttf", + "description": "Font Charis SIL Dan" + }, + { + "name": "libtralo.js", + "description": "File libtralo.js" + }, + { + "name": "libtralo.kvk", + "description": "File libtralo.kvk" + }, + { + "name": "CharisSILDan_FONTLOG.txt", + "description": "File CharisSILDan_FONTLOG.txt" + }, + { + "name": "kmp.inf", + "description": "Package information" + }, + { + "name": "kmp.json", + "description": "Package information (JSON)" + } + ], + "keyboards": [ + { + "name": "TEST1", + "id": "test1", + "version": "1.6.1", + "languages": [ + { + "name": "Bandi", + "id": "bza-Latn" + } + ] + } + ] +} diff --git a/linux/ibus-keyman/src/test/testdata/kmp2.json b/linux/ibus-keyman/src/test/testdata/kmp2.json new file mode 100644 index 00000000000..5e889091f72 --- /dev/null +++ b/linux/ibus-keyman/src/test/testdata/kmp2.json @@ -0,0 +1,127 @@ +{ + "system": { + "keymanDeveloperVersion": "10.0.1202.0", + "fileVersion": "7.0" + }, + "options": { + "readmeFile": "readme.htm" + }, + "info": { + "version": { + "description": "1.6.1" + }, + "name": { + "description": "LIBTRALO" + }, + "copyright": { + "description": "\u00A9 2008-2018 SIL International" + }, + "author": { + "description": "support@keyman.com", + "url": "mailto:support@keyman.com" + } + }, + "files": [ + { + "name": "libtralo.kmx", + "description": "Keyboard LIBTRALO" + }, + { + "name": "readme.htm", + "description": "File readme.htm" + }, + { + "name": "libtralo.png", + "description": "File libtralo.png" + }, + { + "name": "welcome.htm", + "description": "File welcome.htm" + }, + { + "name": "Charis_SIL_README.txt", + "description": "File Charis_SIL_README.txt" + }, + { + "name": "CharisSIL-B.ttf", + "description": "Font Charis SIL Bold" + }, + { + "name": "CharisSIL-BI.ttf", + "description": "Font Charis SIL Bold Italic" + }, + { + "name": "CharisSIL-I.ttf", + "description": "Font Charis SIL Italic" + }, + { + "name": "CharisSIL-R.ttf", + "description": "Font Charis SIL" + }, + { + "name": "FONTLOG.txt", + "description": "File FONTLOG.txt" + }, + { + "name": "OFL.txt", + "description": "File OFL.txt" + }, + { + "name": "OFL-FAQ.txt", + "description": "File OFL-FAQ.txt" + }, + { + "name": "CharisSILDan-Bold.ttf", + "description": "Font Charis SIL Dan Bold" + }, + { + "name": "CharisSILDan-BoldItalic.ttf", + "description": "Font Charis SIL Dan Bold Italic" + }, + { + "name": "CharisSILDan-Italic.ttf", + "description": "Font Charis SIL Dan Italic" + }, + { + "name": "CharisSILDan-Regular.ttf", + "description": "Font Charis SIL Dan" + }, + { + "name": "libtralo.js", + "description": "File libtralo.js" + }, + { + "name": "libtralo.kvk", + "description": "File libtralo.kvk" + }, + { + "name": "CharisSILDan_FONTLOG.txt", + "description": "File CharisSILDan_FONTLOG.txt" + }, + { + "name": "kmp.inf", + "description": "Package information" + }, + { + "name": "kmp.json", + "description": "Package information (JSON)" + } + ], + "keyboards": [ + { + "name": "TEST2", + "id": "test2", + "version": "1.6.1", + "languages": [ + { + "name": "Bom-Kim (Latin)", + "id": "bmf-Latn" + }, + { + "name": "Sherbro (Latin)", + "id": "bun-Latn" + } + ] + } + ] +}