Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions core/src/action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,34 @@ bool km::core::state::set_actions(

return true;
}
using namespace km::core;

namespace {

km_core_usv * duplicate_km_core_usv(const km_core_usv *src) {
if (!src) {
return nullptr;
}
size_t len = 0;
while (src[len]) {
++len;
}
km_core_usv *result = new km_core_usv[len + 1];
std::copy(src, src + len + 1, result);
return result;
}

} // namespace

km_core_actions km::core::clone_actions_object(km_core_actions const &src) {
km_core_actions result;
memset(&result, 0, sizeof(result));
result.code_points_to_delete = src.code_points_to_delete;
result.do_alert = src.do_alert;
result.emit_keystroke = src.emit_keystroke;
result.new_caps_lock_state = src.new_caps_lock_state;
result.deleted_context = duplicate_km_core_usv(src.deleted_context);
result.output = duplicate_km_core_usv(src.output);
result.persist_options = clone_options(src.persist_options);
return result;
}
4 changes: 4 additions & 0 deletions core/src/action.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,9 @@ namespace core
unsigned int code_points_to_delete
);

km_core_actions clone_actions_object(
km_core_actions const &src
);

} // namespace core
} // namespace km
15 changes: 15 additions & 0 deletions core/src/option.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,18 @@ json & km::core::operator << (json &j, abstract_processor const &)

return j;
}

km_core_option_item *
km::core::clone_options(km_core_option_item const *src) {
if (!src) {
return nullptr;
}
size_t count = km_core_options_list_size(src);
km_core_option_item *result = new km_core_option_item[count + 1];
for (size_t i = 0; i < count; ++i) {
km::core::option opt(static_cast<km_core_option_scope>(src[i].scope), src[i].key, src[i].value);
result[i] = opt.release();
}
result[count] = KM_CORE_OPTIONS_END;
return result;
}
2 changes: 1 addition & 1 deletion core/src/option.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ namespace core
return key == nullptr;
}


km_core_option_item * clone_options(km_core_option_item const *src);

} // namespace core
} // namespace km
13 changes: 13 additions & 0 deletions core/src/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@ void state::imx_callback(uint32_t imx_id) {
_imx_callback(static_cast<km_core_state *>(this), imx_id, _imx_object);
}

state::state(state const &other)
: _ctxt(other._ctxt)
, _app_ctxt(other._app_ctxt)
, _processor(other._processor)
, _actions(other._actions)
, _action_struct(clone_actions_object(other._action_struct))
, _debug_items(other._debug_items)
, _imx_callback(other._imx_callback)
, _imx_object(other._imx_object)
, _backspace_handled_internally(other._backspace_handled_internally)
{
}

state::~state() {
km::core::actions_dispose(this->_action_struct);
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class state
public:
state(core::abstract_processor & kb, km_core_option_item const *env);

state(state const &) = default;
state(state const &other);
state(state const &&) = delete;

~state();
Expand Down
183 changes: 179 additions & 4 deletions core/tests/unit/kmnkbd/state_api.tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,136 @@ namespace
return buf;
}

inline
bool action_options_equal(km_core_option_item const * lhs,
km_core_option_item const * rhs)
{
if (lhs == rhs) return true;
if (!lhs || !rhs) return false;

while (lhs->key && rhs->key) {
if (lhs->scope != rhs->scope) return false;
if (std::u16string(lhs->key) != std::u16string(rhs->key)) return false;
if (std::u16string(lhs->value) != std::u16string(rhs->value)) return false;
++lhs;
++rhs;
}

return lhs->key == nullptr && rhs->key == nullptr;
}

inline
bool expect_action_struct(
km_core_actions const & actions,
unsigned int expected_code_points_to_delete,
km_core_usv const * expected_output,
km_core_option_item const * expected_persist_options,
km_core_bool expected_do_alert,
km_core_bool expected_emit_keystroke,
km_core_caps_state expected_new_caps_lock_state,
km_core_usv const * expected_deleted_context
) {
bool all_passed = true;

std::cout << "\n=== Comparing action_struct fields ===" << std::endl;

// code_points_to_delete
std::cout << "code_points_to_delete: " << actions.code_points_to_delete
<< " (expected: " << expected_code_points_to_delete << ")";
if (actions.code_points_to_delete != expected_code_points_to_delete) {
std::cout << " [FAIL]" << std::endl;
all_passed = false;
} else {
std::cout << " [PASS]" << std::endl;
}

// output
std::cout << "output: " << (actions.output ? std::u32string(actions.output) : U"(null)")
<< " expected: " << (expected_output ? std::u32string(expected_output) : U"(null)") << std::endl;
if (expected_output != actions.output) {
if (std::u32string(actions.output) != std::u32string(expected_output)) {
std::cout << " [FAIL]" << std::endl;
all_passed = false;
}
} else {
std::cout << " [PASS]" << std::endl;
}

// persist_options
std::cout << "persist_options comparison:" << std::endl;
if (!action_options_equal(actions.persist_options, expected_persist_options)) {
std::cout << " actual: ";
if (actions.persist_options) {
for (auto opt = actions.persist_options; opt->scope; ++opt) {
std::cout << "[scope=" << opt->scope << ", key=" << opt->key << ", value=" << opt->value << "] ";
}
} else {
std::cout << "(null)";
}
std::cout << std::endl;
std::cout << " expected: ";
if (expected_persist_options) {
for (auto opt = expected_persist_options; opt->key; ++opt) {
std::cout << "[scope=" << opt->scope << ", key=" << opt->key << ", value=" << opt->value << "] ";
}
} else {
std::cout << "(null)";
}
std::cout << " [FAIL]" << std::endl;
all_passed = false;
} else {
std::cout << " [PASS]" << std::endl;
}

// do_alert
std::cout << "do_alert: " << (int)actions.do_alert
<< " (expected: " << (int)expected_do_alert << ")";
if (actions.do_alert != expected_do_alert) {
std::cout << " [FAIL]" << std::endl;
all_passed = false;
} else {
std::cout << " [PASS]" << std::endl;
}

// emit_keystroke
std::cout << "emit_keystroke: " << (int)actions.emit_keystroke
<< " (expected: " << (int)expected_emit_keystroke << ")";
if (actions.emit_keystroke != expected_emit_keystroke) {
std::cout << " [FAIL]" << std::endl;
all_passed = false;
} else {
std::cout << " [PASS]" << std::endl;
}

// new_caps_lock_state
std::cout << "new_caps_lock_state: " << (int)actions.new_caps_lock_state
<< " (expected: " << (int)expected_new_caps_lock_state << ")";
if (actions.new_caps_lock_state != expected_new_caps_lock_state) {
std::cout << " [FAIL]" << std::endl;
all_passed = false;
} else {
std::cout << " [PASS]" << std::endl;
}

// deleted_context
std::cout << "deleted_context: " << (actions.deleted_context ? std::u32string(actions.deleted_context) : U"(null)")
<< " (expected: " << (expected_deleted_context ? std::u32string(expected_deleted_context) : U"(null)") << ")";
if (expected_deleted_context != actions.deleted_context) {
if (std::u32string(actions.deleted_context) != std::u32string(expected_deleted_context)) {
std::cout << " [FAIL]" << std::endl;
all_passed = false;
} else {
std::cout << " [PASS]" << std::endl;
}
} else {
std::cout << " [PASS]" << std::endl;
}

std::cout << "=== End comparison ===" << std::endl << std::endl;

return all_passed;
}

km_core_option_item test_env_opts[] =
{
{u"hello", u"world", 0},
Expand Down Expand Up @@ -168,6 +298,11 @@ int main(int argc, char * argv[])
try_status(km_core_process_event(test_state, KM_CORE_VKEY_L,
KM_CORE_MODIFIER_SHIFT, 1, KM_CORE_EVENT_FLAG_DEFAULT));
test_assert(action_items(test_state, {{KM_CORE_IT_CHAR, {0,}, {km_core_usv('L')}}, {KM_CORE_IT_END}}));

// Without the calling `km_core_state_context_set_if_needed` action struct has a delete for 'L'?
km_core_cu const *state_context = get_context_as_string(km_core_state_context(test_state));
km_core_state_context_set_if_needed(test_state, state_context);

try_status(km_core_process_event(test_state, KM_CORE_VKEY_F2, 0, 1, KM_CORE_EVENT_FLAG_DEFAULT));

km_core_action_item action = {KM_CORE_IT_PERSIST_OPT, {0,}, };
Expand All @@ -187,11 +322,51 @@ int main(int argc, char * argv[])
if (doc1 != doc1_expected) return __LINE__;
if (doc2 != doc2_expected) return __LINE__;

// Destroy them
km_core_state_dispose(test_state);
km_core_state_dispose(test_clone);
km_core_keyboard_dispose(test_kb);
// Test the action_struct values for the active and cloned states are
// independent and match their respective expected values.
const unsigned int expected_state_code_points_to_delete = 0;
const km_core_usv * expected_state_output = U"";
const km_core_bool expected_state_do_alert = KM_CORE_FALSE;
const km_core_bool expected_state_emit_keystroke = KM_CORE_FALSE;
const km_core_caps_state expected_state_new_caps_lock_state = KM_CORE_CAPS_UNCHANGED;
km_core_option_item expected_options[] = {expected_persist_opt, KM_CORE_OPTIONS_END };
const km_core_usv * expected_deleted_text = U"";
// Cloned expected values
const unsigned int clone_state_code_points_to_delete = 0;
const km_core_usv * clone_state_output = U"";
const km_core_bool clone_state_do_alert = KM_CORE_FALSE;
const km_core_bool clone_state_emit_keystroke = KM_CORE_FALSE;
const km_core_caps_state clone_state_new_caps_lock_state = KM_CORE_CAPS_UNCHANGED;
km_core_option_item clone_state_options[] = {KM_CORE_OPTIONS_END};
const km_core_usv * clone_state_deleted_text = nullptr;

const auto & state_actions = test_state->action_struct();
const auto & clone_actions = test_clone->action_struct();

test_assert (expect_action_struct(state_actions,
expected_state_code_points_to_delete,
expected_state_output,
expected_options,
expected_state_do_alert,
expected_state_emit_keystroke,
expected_state_new_caps_lock_state,
expected_deleted_text
));

test_assert (expect_action_struct(clone_actions,
clone_state_code_points_to_delete,
clone_state_output,
clone_state_options,
clone_state_do_alert,
clone_state_emit_keystroke,
clone_state_new_caps_lock_state,
clone_state_deleted_text
));

// Destroy them
km_core_state_dispose(test_state);
km_core_state_dispose(test_clone);
km_core_keyboard_dispose(test_kb);

return 0;
}
Loading