1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
|
/*
Copyright: © 2021 SIL International.
Description: Tests for key rules list generation in the kmx processor
Create Date: 30 Oct 2018
Authors: Ross Cruickshank (RC) Marc Durdin (MD), Tim Eves (TSE)
*/
#include <kmx/kmx_processevent.h>
#include "path.hpp"
#include "state.hpp"
#include "../kmnkbd/action_items.hpp"
#include <test_assert.h>
#include <test_color.h>
#include "../emscripten_filesystem.h"
#include "../load_kmx_file.hpp"
#include "utfcodec.hpp"
#include <map>
#include <iostream>
/** This test will test the infrastructure around IMX third party librays
* and callback. The functions tested are:
* que_actions
* get_intermediate_context
*/
using namespace km::core::kmx;
km_core_option_item test_env_opts[] =
{
KM_CORE_OPTIONS_END
};
std::map<uint16_t,std::string> expected_imx_map { {3, "imsample.dll:DF"},
{4, "imsample.dll:func2"},
{5, "another.dll:func3"},
{6, "another.dll:func4"},
};
std::map<uint16_t,std::string> g_extract_imx_map;
int error_args() {
std::cerr << "kmx: Not enough arguments." << std::endl;
return 1;
}
extern "C"
{
/**
* This callback will be used in place of a callback to the third party
* library. It will then test adding to the action queue all the implemented action types.
* KM_CORE_IT_END
* KM_CORE_IT_CHAR
* KM_CORE_IT_MARKER
* KM_CORE_IT_ALERT
* KM_CORE_IT_BACK
* KM_CORE_IT_INVALIDATE_CONTEXT
*
*
* @param state
* @param imx_id
* @param callback_object
* @return uint8_t
*/
uint8_t test_imx_callback(km_core_state *state, uint32_t imx_id, void *callback_object) {
std::cout << "test_imx_callback imx_id: " << imx_id << std::endl;
if (callback_object==nullptr) {
return FALSE;
}
// Test get intermediate context
km_core_context_item *entry_context = nullptr;
km_core_state_get_intermediate_context(state, &entry_context);
size_t n = 0;
try_status(context_items_to_utf16(entry_context, nullptr, &n))
km_core_cu *buf = new km_core_cu[n];
try_status(context_items_to_utf16(entry_context, buf, &n));
std::cout << "imx entry context : " << " [" << buf << "]" << std::endl;
km_core_context_items_dispose(entry_context);
delete[] buf;
// Test km_core_state_queue_action_items but also test identifying the unique
// number assigned to each 3rd party library function name.
std::map<uint16_t,std::string> *imx_map = static_cast<std::map<uint16_t,std::string>*>(callback_object);
std::map<uint16_t,std::string>::iterator it;
it = (*imx_map).find(imx_id);
if (it == (*imx_map).end()) {
std::cerr << "Unique store number is not in the engines imx list" << std::endl;
return FALSE;
}
std::cout << "test_imx_callback function name: " << it->second << std::endl;
km_core_action_item *a_items = new km_core_action_item[10];
switch (imx_id) {
// this corresponds to store index; if these change, the values
// here will need updating
case 3:
a_items[0].type = KM_CORE_IT_CHAR;
a_items[0].character = km_core_usv('X');
a_items[1].type = KM_CORE_IT_ALERT;
a_items[2].type = KM_CORE_IT_END;
km_core_state_queue_action_items(state, a_items);
break;
case 4:
a_items[0].type = KM_CORE_IT_CHAR;
a_items[0].character = km_core_usv('Y');
a_items[1].type = KM_CORE_IT_MARKER;
a_items[1].marker = 1;
a_items[2].type = KM_CORE_IT_CHAR;
a_items[2].character = km_core_usv('A');
a_items[3].type = KM_CORE_IT_END;
km_core_state_queue_action_items(state, a_items);
break;
case 5:
a_items[0].type = KM_CORE_IT_BACK;
a_items[0].backspace.expected_type = KM_CORE_BT_CHAR;
a_items[0].backspace.expected_value = km_core_usv('A');
a_items[1].type = KM_CORE_IT_CHAR;
a_items[1].character = km_core_usv('Z');
a_items[2].type = KM_CORE_IT_END;
km_core_state_queue_action_items(state, a_items);
break;
case 6: {
a_items[0].type = KM_CORE_IT_INVALIDATE_CONTEXT;
a_items[1].type = KM_CORE_IT_END;
km_core_state_queue_action_items(state, a_items);
}
break;
default:
break;
}
if (a_items) {
delete[] a_items;
}
// Test Exit Context
km_core_context_item *exit_context = nullptr;
km_core_state_get_intermediate_context(state, &exit_context);
n = 0;
try_status(context_items_to_utf16(exit_context, nullptr, &n))
km_core_cu *tmp_buf = new km_core_cu[n];
try_status(context_items_to_utf16(exit_context, tmp_buf, &n));
std::cout << "imx exit context : " << " [" << tmp_buf << "]" << std::endl;
km_core_context_items_dispose(exit_context);
delete[] tmp_buf;
return 1;
}
}; // extern "C"
void test_imx_list(const km::core::path &source_file){
km_core_keyboard * test_kb = nullptr;
km_core_state * test_state = nullptr;
km_core_keyboard_imx * kb_imx_list;
km::core::path full_path = source_file;
auto blob = km::tests::load_kmx_file(full_path.native().c_str());
try_status(km_core_keyboard_load_from_blob(full_path.stem().c_str(), blob.data(), blob.size(), &test_kb));
// Setup state, environment
try_status(km_core_state_create(test_kb, test_env_opts, &test_state));
try_status(km_core_keyboard_get_imx_list(test_kb, &kb_imx_list));
// This keyboard has 4 function names from 2 different libraries in the stores
km_core_keyboard_imx *imx_rule_it = kb_imx_list;
auto x = 0;
for (; imx_rule_it->library_name; ++imx_rule_it) {
std::u16string str;
str.append(imx_rule_it->library_name);
str.append(u":");
str.append(imx_rule_it->function_name);
auto extracted_library_function = convert<km_core_cu,char>(str);
test_assert(extracted_library_function == expected_imx_map[imx_rule_it->imx_id] );
g_extract_imx_map[imx_rule_it->imx_id] = extracted_library_function;
++x;
}
std::cout << " X Value is " << x << std::endl;
test_assert(x==4);
km_core_keyboard_imx_list_dispose(kb_imx_list);
km_core_state_dispose(test_state);
km_core_keyboard_dispose(test_kb);
}
// This tests both the registering callbacks and queuing actions.
// The sequence for this test will be to load a keyboard and register
// a callback - `test_imx_callback`.
// Then it will call `km_core_process_event` with a `key` that will cause
// the callback to be called. The callback will then use
// `km_core_state_queue_action_items` to queue action times so that
// the kmx processor will add this items to its action queue.
// Finally when the `process_event` call returns we verify the action
// queue is as expected.
void test_queue_actions (const km::core::path &source_keyboard) {
km_core_keyboard * test_kb = nullptr;
km_core_state * test_state = nullptr;
km_core_keyboard_imx * kb_imx_list;
km::core::path full_path = source_keyboard;
auto blob = km::tests::load_kmx_file(full_path.native().c_str());
try_status(km_core_keyboard_load_from_blob(full_path.stem().c_str(), blob.data(), blob.size(), &test_kb));
// Setup state, environment
try_status(km_core_state_create(test_kb, test_env_opts, &test_state));
try_status(km_core_keyboard_get_imx_list(test_kb, &kb_imx_list));
km_core_state_imx_register_callback(test_state, test_imx_callback, (void*)&g_extract_imx_map);
// Key Press that doesn't trigger a call back
try_status(km_core_process_event(test_state, KM_CORE_VKEY_S,KM_CORE_MODIFIER_SHIFT, 1, KM_CORE_EVENT_FLAG_DEFAULT));
test_assert(action_items(test_state, {{KM_CORE_IT_CHAR, {0,}, {km_core_usv('S')}}, {KM_CORE_IT_END}}));
try_status(km_core_process_event(test_state, KM_CORE_VKEY_BKSP, 0, 1, KM_CORE_EVENT_FLAG_DEFAULT));
test_assert(action_items(test_state, {{KM_CORE_IT_CHAR, {0,}, {km_core_usv('X')}}, {KM_CORE_IT_ALERT, {0,}, {0}}, {KM_CORE_IT_END}}));
try_status(km_core_process_event(test_state, KM_CORE_VKEY_ESC, 0, 1, KM_CORE_EVENT_FLAG_DEFAULT));
test_assert(action_items(test_state, { {KM_CORE_IT_CHAR, {0,}, {km_core_usv('Y')}},
{KM_CORE_IT_MARKER, {0,}, {1}},
{KM_CORE_IT_CHAR, {0,}, {km_core_usv('A')}},
{KM_CORE_IT_END}}));
try_status(km_core_process_event(test_state, KM_CORE_VKEY_1, 0, 1, KM_CORE_EVENT_FLAG_DEFAULT));
km_core_action_item bksp_a = {KM_CORE_IT_BACK};
bksp_a.backspace.expected_type = KM_CORE_BT_CHAR;
bksp_a.backspace.expected_value = 'A';
test_assert(action_items(test_state, {bksp_a,{KM_CORE_IT_CHAR, {0,}, {km_core_usv('Z')}}, {KM_CORE_IT_END}}));
try_status(km_core_process_event(test_state, KM_CORE_VKEY_2, 0, 1, KM_CORE_EVENT_FLAG_DEFAULT));
test_assert(action_items(test_state, {{KM_CORE_IT_INVALIDATE_CONTEXT, {0,}, {0}}, {KM_CORE_IT_END}}));
km_core_state_imx_deregister_callback(test_state);
km_core_keyboard_imx_list_dispose(kb_imx_list);
km_core_state_dispose(test_state);
km_core_keyboard_dispose(test_kb);
}
int main(int argc, char *argv []) {
int first_arg = 1;
if (argc < 2) {
return error_args();
}
auto arg_color = std::string(argv[1]) == "--color";
if(arg_color) {
first_arg++;
if(argc < 3) {
return error_args();
}
}
console_color::enabled = console_color::isaterminal() || arg_color;
km::core::kmx::g_debug_ToConsole = TRUE;
#ifdef __EMSCRIPTEN__
test_imx_list(get_wasm_file_path(argv[first_arg]));
test_queue_actions(get_wasm_file_path(argv[first_arg]));
#else
test_imx_list(argv[first_arg]);
test_queue_actions(argv[first_arg]);
#endif
return 0;
}
|