Skip to content

Commit

Permalink
Merge pull request #10305 from keymanapp/chore/core/ldml-test-robust-…
Browse files Browse the repository at this point in the history
…epic-ldml

chore(core): better reporting in ldml tests 🙀
  • Loading branch information
srl295 authored Jan 3, 2024
2 parents f42f759 + 84c218f commit a6e4721
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 60 deletions.
77 changes: 50 additions & 27 deletions core/tests/unit/ldml/ldml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,13 +260,19 @@ run_test(const km::core::path &source, const km::core::path &compiled, km::tests
// verify at beginning
verify_context(text_store, test_state, test_context);

int errorLine = 0; // nonzero if err.

// Run through actions, applying output for each event
for (test_source.next_action(action); action.type != km::tests::LDML_ACTION_DONE; test_source.next_action(action)) {
// handle backspace here
if (action.type == km::tests::LDML_ACTION_KEY_EVENT) {
do {
test_source.next_action(action);
switch (action.type) {
case km::tests::LDML_ACTION_DONE:
// We'll get a printout when this loop exits.
break;
case km::tests::LDML_ACTION_KEY_EVENT: {
auto &p = action.k;
std::cout << "- key action: " << km::core::kmx::Debug_VirtualKey(p.vk) << "/modifier " << km::core::kmx::Debug_ModifierName(p.modifier_state) << " 0x" << p.modifier_state
<< std::dec << std::endl;
std::cout << "- key action: " << km::core::kmx::Debug_VirtualKey(p.vk) << "/modifier "
<< km::core::kmx::Debug_ModifierName(p.modifier_state) << " 0x" << p.modifier_state << std::dec << std::endl;
// Because a normal system tracks caps lock state itself,
// we mimic that in the tests. We assume caps lock state is
// updated on key_down before the processor receives the
Expand All @@ -277,17 +283,20 @@ run_test(const km::core::path &source, const km::core::path &compiled, km::tests

for (auto key_down = 1; key_down >= 0; key_down--) {
// expected error only applies to key down
try_status(km_core_process_event(test_state, p.vk, p.modifier_state | test_source.caps_lock_state(), key_down, KM_CORE_EVENT_FLAG_DEFAULT)); // TODO-LDML: for now. Should send touch and hardware events.
try_status(km_core_process_event(
test_state, p.vk, p.modifier_state | test_source.caps_lock_state(), key_down,
KM_CORE_EVENT_FLAG_DEFAULT)); // TODO-LDML: for now. Should send touch and hardware events.

for (auto act = km_core_state_action_items(test_state, nullptr); act->type != KM_CORE_IT_END; act++) {
apply_action(test_state, *act, text_store, test_context, test_source, test_context);
}
}
verify_context(text_store, test_state, test_context);
} else if (action.type == km::tests::LDML_ACTION_EMIT_STRING) {
} break;
case km::tests::LDML_ACTION_EMIT_STRING: {
std::cout << "- string emit action: " << action.string << std::endl;
std::cerr << "TODO-LDML: note, LDML_ACTION_EMIT_STRING is NOT going through keyboard, transforms etc." << std::endl;
text_store.append(action.string); // TODO-LDML: not going through keyboard
text_store.append(action.string); // TODO-LDML: not going through keyboard
// Now, update context?
km_core_context_item *nitems = nullptr;
try_status(km_core_context_items_from_utf16(action.string.c_str(), &nitems));
Expand All @@ -299,37 +308,51 @@ run_test(const km::core::path &source, const km::core::path &compiled, km::tests
km_core_context_items_dispose(nitems);

verify_context(text_store, test_state, test_context);
} else if (action.type == km::tests::LDML_ACTION_CHECK_EXPECTED) {
assert(km::core::ldml::normalize_nfd(action.string)); // TODO-LDML: should be NFC
} break;
case km::tests::LDML_ACTION_CHECK_EXPECTED: {
assert(km::core::ldml::normalize_nfd(action.string)); // TODO-LDML: should be NFC
std::cout << "- check expected" << std::endl;
std::cout << "expected : " << string_to_hex(action.string) << " [" << action.string << "]" << std::endl;
std::cout << "text store: " << string_to_hex(text_store) << " [" << text_store << "]" << std::endl;
// Compare internal context with expected result
if (text_store != action.string) return __LINE__;
} else if (action.type == km::tests::LDML_ACTION_FAIL) {
if (text_store != action.string) {
errorLine = __LINE__;
}
} break;
case km::tests::LDML_ACTION_FAIL: {
// test requested failure
std::cout << "- FAIL: " << action.string << std::endl;
return __LINE__;
} else {
std::wcout << console_color::fg(console_color::BRIGHT_RED) << "- FAIL: " << action.string << console_color::reset()
<< std::endl;
errorLine = __LINE__;
} break;
case km::tests::LDML_ACTION_SKIP: {
// test requested skip
std::wcout << console_color::fg(console_color::YELLOW) << "- SKIP: " << action.string << console_color::reset()
<< std::endl;
} break;
default:
std::cerr << " Err: unhandled action type " << action.type << std::endl;
return __LINE__;
errorLine = __LINE__;
}
}
std::cout << "- DONE" << std::endl;

// Test if the beep action was as expected
if (g_beep_found != test_source.get_expected_beep())
return __LINE__;

} while (!action.done() && errorLine == 0);

// re-verify at end.
verify_context(text_store, test_state, test_context);
if (errorLine != 0) {
// re-verify at end (if there wasn't already a failure)
verify_context(text_store, test_state, test_context);
}

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

return 0;
if (g_beep_found != test_source.get_expected_beep()) {
// Test if the beep action was as expected.
// TODO-LDML: possible in LDML?
std::wcout << console_color::fg(console_color::BRIGHT_RED) << "- FAIL - did not get expected beep" << console_color::reset() << std::endl;
return __LINE__;
}

return errorLine;
}

/**
Expand Down
102 changes: 69 additions & 33 deletions core/tests/unit/ldml/ldml_test_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,59 @@ namespace tests {
#include <test_color.h>



/** string munging */
static void append_to_str(std::u16string &str, const char *buf) {
const PKMX_WCHAR p = km::core::kmx::strtowstr((char *)buf); /** cast away const, unused*/
const std::u16string p2(p);
str.append(p2);
delete [] p;
}

/** string munging */
static void append_to_str(std::u16string &str, long n) {
char buf[64];

snprintf(buf, 64, "%ld", n);
append_to_str(str, buf);
}

void
ldml_action::formatType(const char *f, int l, ldml_action_type setType, const std::u16string &msg) {
type = setType;
string.clear();
append_to_str(string, f);
string.append(u":");
append_to_str(string, l);
string.append(u" ");
string.append(msg);
}

void
ldml_action::formatType(const char *f, int l, ldml_action_type setType, const std::u16string &msg, const std::u16string &msg2) {
std::u16string tmp = msg;
tmp.append(msg2);
formatType(f, l, setType, tmp);
}

void
ldml_action::formatType(const char *f, int l, ldml_action_type setType, const std::u16string &msg, long msg2) {
std::u16string tmp;
append_to_str(tmp, msg2);
formatType(f, l, setType, msg, tmp);
}

void
ldml_action::formatType(const char *f, int l, ldml_action_type setType, const std::u16string &msg, const std::string &msg2) {
std::u16string tmp;
append_to_str(tmp, msg2.c_str());
formatType(f, l, setType, msg, tmp);
}

bool ldml_action::done() const {
return (type == LDML_ACTION_DONE || type == LDML_ACTION_SKIP || type == LDML_ACTION_FAIL);
}

LdmlTestSource::LdmlTestSource() {
}

Expand Down Expand Up @@ -396,10 +449,8 @@ class LdmlJsonTestSource : public LdmlTestSource {
*/
std::size_t action_index = -1;
const km::core::kmx::kmx_plus *kmxplus;
/**
* Helpers
*/
void set_key_from_id(key_event& k, const std::u16string& id);
/** @return false if not found */
bool set_key_from_id(key_event& k, const std::u16string& id);
};

LdmlJsonTestSource::LdmlJsonTestSource(const std::string &path, km::core::kmx::kmx_plus *k)
Expand All @@ -410,32 +461,25 @@ LdmlJsonTestSource::LdmlJsonTestSource(const std::string &path, km::core::kmx::k
LdmlJsonTestSource::~LdmlJsonTestSource() {
}

void LdmlJsonTestSource::set_key_from_id(key_event& k, const std::u16string& id) {
bool LdmlJsonTestSource::set_key_from_id(key_event& k, const std::u16string& id) {
k = {0, 0}; // set to a null value at first.

assert(kmxplus != nullptr);
// lookup the id
assert(kmxplus->key2 != nullptr);

assert(kmxplus->key2Helper.valid());

// TODO-LDML: optimize. or optimise.

// First, find the string
KMX_DWORD strId = kmxplus->strs->find(id);
if (strId == 0) {
// will also get here if id is empty.
std::cerr << "ERROR: could not find string for " << id << std::endl;
assert(false);
return;
return false;
}

// OK. Now we can search the keybag
KMX_DWORD keyIndex = 0; // initialize loop
auto *key2 = kmxplus->key2Helper.findKeyByStringId(strId, keyIndex);
assert(key2 != nullptr);
if (key2 == nullptr) {
return;
return false;
}

// Now, look for the _first_ candidate vkey match in the kmap.
Expand All @@ -444,11 +488,11 @@ void LdmlJsonTestSource::set_key_from_id(key_event& k, const std::u16string& id)
assert(kmap != nullptr);
if (kmap->key == keyIndex) {
k = {(km_core_virtual_key)kmap->vkey, (uint16_t)kmap->mod};
return;
return true;
}
}
// Else, unfound
return;
return false;
}


Expand Down Expand Up @@ -478,7 +522,9 @@ LdmlJsonTestSource::next_action(ldml_action &fillin) {
fillin.type = LDML_ACTION_KEY_EVENT;
auto keyId = LdmlTestSource::parse_u8_source_string(key.get<std::string>());
// now, look up the key
set_key_from_id(fillin.k, keyId);
if (!set_key_from_id(fillin.k, keyId)) {
fillin.formatType(__FILE__, __LINE__, LDML_ACTION_FAIL, u"Could not find key: ", keyId);
}
return;
} else if (type == "emit") {
fillin.type = LDML_ACTION_EMIT_STRING;
Expand All @@ -493,9 +539,9 @@ LdmlJsonTestSource::next_action(ldml_action &fillin) {
return;
}

// TODO-LDML: error passthrough
std::cerr << "TODO-LDML: Error, unknown/unhandled action: " << type << std::endl;
fillin.type = LDML_ACTION_DONE;
// unhandled, so fail
fillin.formatType(__FILE__, __LINE__, LDML_ACTION_FAIL, u"Error, unknown/unhandled action: ", (long)type);
return;
}

const std::u16string &
Expand Down Expand Up @@ -535,10 +581,6 @@ class LdmlJsonRepertoireTestSource : public LdmlTestSource {
std::unique_ptr<icu::UnicodeSetIterator> iterator;
bool need_check = false; // set this after each char
const km::core::kmx::kmx_plus *kmxplus;
/**
* Helpers
*/
void set_key_from_id(key_event& k, const std::u16string& id);
};

LdmlJsonRepertoireTestSource::LdmlJsonRepertoireTestSource(const std::string &path, km::core::kmx::kmx_plus *k)
Expand All @@ -552,13 +594,11 @@ LdmlJsonRepertoireTestSource::~LdmlJsonRepertoireTestSource() {
void
LdmlJsonRepertoireTestSource::next_action(ldml_action &fillin) {
if (type != "simple") {
std::cerr << "TODO-LDML: Warning: only 'simple' is supported now, not " << type << std::endl;
fillin.type = LDML_ACTION_DONE;
fillin.formatType(__FILE__, __LINE__, LDML_ACTION_SKIP, u"TODO-LDML: Only 'simple' is supported, not ", type);
return;
}

if (!iterator->next()) {
std::cout << "TODO-LDML: end of unicode set iterator" << std::endl;
fillin.type = LDML_ACTION_DONE;
return;
}
Expand Down Expand Up @@ -605,9 +645,7 @@ LdmlJsonRepertoireTestSource::next_action(ldml_action &fillin) {
KMX_DWORD keyIndex = 0;
auto *key2 = kmxplus->key2Helper.findKeyByStringTo(chstr, strId, keyIndex);
if (key2 == nullptr) {
fillin.string = u"No key for repertoire test: ";
fillin.string.append(chstr);
fillin.type = LDML_ACTION_FAIL;
fillin.formatType(__FILE__, __LINE__, LDML_ACTION_FAIL, u"No key for repertoire test: ", chstr);
return;
}

Expand All @@ -623,9 +661,7 @@ LdmlJsonRepertoireTestSource::next_action(ldml_action &fillin) {
}
}

fillin.type = LDML_ACTION_FAIL;
fillin.string = u"Could not find candidate vkey: ";
fillin.string.append(chstr);
fillin.formatType(__FILE__, __LINE__, LDML_ACTION_FAIL, u"Could not find candidate vkey: ", chstr);
}

const std::u16string &
Expand Down
16 changes: 16 additions & 0 deletions core/tests/unit/ldml/ldml_test_source.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ enum ldml_action_type {
* Done. no more actions
*/
LDML_ACTION_DONE,
/**
* Skip this test
*/
LDML_ACTION_SKIP,
/**
* key_event - a vkey
*/
Expand All @@ -45,6 +49,18 @@ struct ldml_action {
ldml_action_type type;
key_event k;
std::u16string string;

/** mark failure as specified type */
void formatType(const char *file, int line, ldml_action_type type, const std::u16string &msg);
/** mark failure as specified type. msg2 is concatenated */
void formatType(const char *file, int line, ldml_action_type type, const std::u16string &msg, const std::u16string &msg2);
/** mark failure as specified type. msg2 is concatenated */
void formatType(const char *file, int line, ldml_action_type type, const std::u16string &msg, long msg2);
/** mark failure as specified type. msg2 is concatenated */
void formatType(const char *file, int line, ldml_action_type type, const std::u16string &msg, const std::string &msg2);

/** @returns true if caller should stop processing events */
bool done() const;
};

/**
Expand Down

0 comments on commit a6e4721

Please sign in to comment.