Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(core): dx: ldml test improvement, backspace test 🙀 #9759

Merged
merged 8 commits into from
Oct 20, 2023
13 changes: 5 additions & 8 deletions common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,15 +393,12 @@ export class LDMLKeyboardXMLSourceFileReader {
r.stuffBoxes(test, $$, 'startContext'); // singleton
// now the actions
test.actions = $$.map(v => {
const subtag = v['#name'];
const subv = LDMLKeyboardXMLSourceFileReader.defaultMapper(v, r);
switch(subtag) {
case 'keystroke': return { keystroke: subv };
case 'check': return { check: subv };
case 'emit': return { emit: subv };
case 'startContext': return null; // handled above
default: this.callbacks.reportMessage(CommonTypesMessages.Error_TestDataUnexpectedAction({ subtag })); return null;
const type = v['#name']; // element name
if (type === 'startContext') {
return null; // handled above
}
const subv = LDMLKeyboardXMLSourceFileReader.defaultMapper(v, r);
return Object.assign({ type }, subv);
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved
}).filter(v => v !== null);
return test;
});
Expand Down
15 changes: 15 additions & 0 deletions core/tests/unit/ldml/keyboards/k_006_backspace-test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboardTest3.dtd">
<keyboardTest3 conformsTo="techpreview">
<info keyboard="k_006_backspace.xml" author="Team Keyboard" name="backspace" />
<tests name="backspace">
<test name="regex-test-0-a">
<startContext to="" />
<keystroke key="t" />
<keystroke key="a" />
<keystroke key="b" />
<backspace />
<check result="ta" /> <!-- TODO-LDML: #9760 Should be 't' due to backspace transform -->
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test verifies that <backspace /> works at all, #9760 to fix it for transforms

</test>
</tests>
</keyboardTest3>
7 changes: 0 additions & 7 deletions core/tests/unit/ldml/keyboards/k_006_backspace.xml
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
from https://github.com/unicode-org/cldr/blob/keyboard-preview/docs/ldml/tr35-keyboards.md#element-transform

@@keys: [K_T][K_A][K_B][K_BKSP]
@@expected: t

-->
<!DOCTYPE keyboard3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard3.dtd">
<keyboard3 locale="en" conformsTo="techpreview">
<info author="srl295" indicator="🙀" layout="qwerty" normalization="NFC" />
Expand Down
2 changes: 1 addition & 1 deletion core/tests/unit/ldml/keyboards/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ tests_without_testdata = [
'k_003_transform',
'k_004_tinyshift',
'k_005_modbittest',
# 'k_006_backspace', ## not quite there yet. TODO-LDML
'k_010_mt',
'k_011_mt_iso',
'k_100_keytest',
Expand All @@ -33,6 +32,7 @@ tests_without_testdata = [
# These tests have a k_001_tiny-test.xml file as well.
tests_with_testdata = [
'k_001_tiny',
'k_006_backspace',
'k_007_transform_rgx',
'k_008_transform_norm',
'k_020_fr', # TODO-LDML: move to cldr above (fix vkey)
Expand Down
64 changes: 47 additions & 17 deletions core/tests/unit/ldml/ldml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ run_test(const km::kbp::path &source, const km::kbp::path &compiled, km::tests::

// 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) {
auto &p = action.k;
std::cout << "- key action: 0x" << std::hex << p.vk << "/modifier 0x" << p.modifier_state << std::dec << std::endl;
Expand Down Expand Up @@ -294,17 +295,25 @@ run_test(const km::kbp::path &source, const km::kbp::path &compiled, km::tests::
/**
* Run all tests for this keyboard
*/
int run_all_tests(const km::kbp::path &source, const km::kbp::path &compiled) {
int run_all_tests(const km::kbp::path &source, const km::kbp::path &compiled, const std::string &filter) {
std::wcout << console_color::fg(console_color::BLUE) << "source file = " << source << std::endl
<< "compiled file = " << compiled << console_color::reset() << std::endl;
if(!filter.empty()) {
std::wcout << "Running only tests matching (substring search): " << filter.c_str() << std::endl;
}

km::tests::LdmlEmbeddedTestSource embedded_test_source;

std::vector<std::string> failures; // track failures for summary

int embedded_result = embedded_test_source.load_source(source);

if (embedded_result == 0) {
if (!filter.empty()) {
// Always skip the embedded test if there's a filter.
std::wcout << console_color::fg(console_color::YELLOW) << "SKIP: " << source.name() << " (embedded)" << console_color::reset()
<< std::endl;
embedded_result = 0; // no error
} else if (embedded_result == 0) {
// embedded loaded OK, try it
std::wcout << console_color::fg(console_color::BLUE) << console_color::bold() << "TEST: " << source.name() << " (embedded)"
<< console_color::reset() << std::endl;
Expand All @@ -325,31 +334,43 @@ int run_all_tests(const km::kbp::path &source, const km::kbp::path &compiled) {
if (json_result != -1) {
const km::tests::JsonTestMap& json_tests = json_factory.get_tests();

size_t skip_count = 0;
assert(json_tests.size() > 0);
// Loop over all tests
for (const auto& n : json_tests) {
std::wcout << console_color::fg(console_color::BLUE) << console_color::bold() << "TEST: " << json_path.stem().c_str() << "/" << n.first.c_str() << console_color::reset() << std::endl;
const auto test_name = n.first;
auto qq = test_name.find(filter);
if (filter == "--list" || (qq == std::string::npos)) {
skip_count ++;
std::wcout << console_color::fg(console_color::YELLOW) << "SKIP: " << json_path.stem().c_str() << "/" << console_color::bold() << n.first.c_str() << console_color::reset() << std::endl;
continue;
}
std::wcout << console_color::fg(console_color::BLUE) << "TEST: " << json_path.stem().c_str() << "/" << console_color::bold() << n.first.c_str() << console_color::reset() << std::endl;
int sub_test = run_test(source, compiled, *n.second);
if (sub_test != 0) {
std::wcout << console_color::fg(console_color::BRIGHT_RED) << "FAIL: " << json_path.stem() << "/" << n.first.c_str()
std::wcout << console_color::fg(console_color::BRIGHT_RED) << "FAIL: " << json_path.stem() << "/" << console_color::bold() << n.first.c_str()
<< console_color::reset() << std::endl;
failures.push_back(json_path.stem() + "/" + n.first);
json_result = sub_test; // set to last failure
} else {
std::wcout << console_color::fg(console_color::GREEN) << " PASS: " << console_color::reset() << json_path.stem()
<< "/" << n.first.c_str() << std::endl;
std::wcout << console_color::fg(console_color::GREEN) << "PASS: " << console_color::reset() << json_path.stem()
<< "/" << console_color::bold() << n.first.c_str() << std::endl;
}
}
auto all_count = json_tests.size();
auto all_count = json_tests.size();
auto fail_count = failures.size();
auto pass_count = all_count - fail_count;
auto pass_count = all_count - fail_count - skip_count;
if (pass_count > 0) {
std::wcout << console_color::fg(console_color::GREEN) << " +" << pass_count;
}
if (fail_count > 0) {
std::wcout << console_color::fg(console_color::BRIGHT_RED) <<
" -" << fail_count;
}
if (skip_count > 0) {
std::wcout << console_color::fg(console_color::YELLOW) <<
" (skipped " << skip_count << ")";
}
std::wcout << console_color::reset() << " of " << all_count << " JSON tests in "
<< json_path.stem() << std::endl;
}
Expand Down Expand Up @@ -386,10 +407,11 @@ int run_all_tests(const km::kbp::path &source, const km::kbp::path &compiled) {

constexpr const auto help_str =
"\
ldml [--color] <LDML_TEST_FILE> <KMX_FILE>\n\
ldml [--color] <LDML_FILE> <KMX_FILE> [ <TEST_FILTER> | --list ]\n\
help:\n\
\tKMN_FILE:\tThe ldml test file for the keyboard under test.\n\
\tKMX_FILE:\tThe corresponding compiled kmx file.\n";
\tLDML_FILE:\tThe .xml file for the keyboard under test.\n\
\tKMX_FILE:\tThe corresponding compiled kmx file.\n\
\tTEST_FILTER:\tIf present, only run json tests containing the filter substring. --list will list all tests\n";

} // namespace

Expand All @@ -402,20 +424,28 @@ int error_args() {
int main(int argc, char *argv[]) {
int first_arg = 1;

if (argc < 3) {
if ((argc - first_arg) < 2) { // if < 2 remaining args
return error_args();
}

auto arg_color = std::string(argv[1]) == "--color";
auto arg_color = std::string(argv[first_arg]) == "--color";
if(arg_color) {
first_arg++;
if(argc < 4) {
return error_args();
}
}
console_color::enabled = console_color::isaterminal() || arg_color;

int rc = run_all_tests(argv[first_arg], argv[first_arg + 1]);
if ((argc - first_arg) < 2) {
return error_args();
}
const km::kbp::path ldml_file = argv[first_arg++];
const km::kbp::path kmx_file = argv[first_arg++];

std::string filter; // default to 'all tests'
if ((argc - first_arg) >= 1) {
filter = argv[first_arg++];
}

int rc = run_all_tests(ldml_file, kmx_file, filter);
if (rc != EXIT_SUCCESS) {
std::wcerr << console_color::fg(console_color::BRIGHT_RED) << "FAILED" << console_color::reset() << std::endl;
rc = EXIT_FAILURE;
Expand Down
40 changes: 22 additions & 18 deletions core/tests/unit/ldml/ldml_test_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,40 +462,42 @@ LdmlJsonTestSource::next_action(ldml_action &fillin) {

action_index++;
auto action = data["/actions"_json_pointer].at(action_index);
// load up several common attributes
auto type = action["/type"_json_pointer];
auto result = action["/result"_json_pointer];
auto key = action["/key"_json_pointer];
auto to = action["/to"_json_pointer];

// is it a check event?
auto as_check = action["/check/result"_json_pointer];
if (as_check.is_string()) {
if (type == "check") {
fillin.type = LDML_ACTION_CHECK_EXPECTED;
fillin.string = LdmlTestSource::parse_u8_source_string(as_check.get<std::string>());
fillin.string = LdmlTestSource::parse_u8_source_string(result.get<std::string>());
UErrorCode status = U_ZERO_ERROR;
km::kbp::ldml::normalize_nfc(fillin.string, status);
assert(U_SUCCESS(status));
return;
}

// is it a keystroke by id?
auto as_key = action["/keystroke/key"_json_pointer];
if (as_key.is_string()) {
} else if (type == "keystroke") {
fillin.type = LDML_ACTION_KEY_EVENT;
auto keyId = LdmlTestSource::parse_u8_source_string(as_key.get<std::string>());
auto keyId = LdmlTestSource::parse_u8_source_string(key.get<std::string>());
// now, look up the key
set_key_from_id(fillin.k, keyId);
return;
}
// TODO-LDML: handle gesture, etc

auto as_emit = action["/emit/to"_json_pointer];
if (as_emit.is_string()) {
} else if (type == "emit") {
fillin.type = LDML_ACTION_EMIT_STRING;
fillin.string = LdmlTestSource::parse_u8_source_string(as_emit.get<std::string>());
fillin.string = LdmlTestSource::parse_u8_source_string(to.get<std::string>());
UErrorCode status = U_ZERO_ERROR;
km::kbp::ldml::normalize_nfc(fillin.string, status);
return;
} else if (type == "backspace") {
// backspace is handled as a key event
fillin.type = LDML_ACTION_KEY_EVENT;
fillin.k.modifier_state = 0;
fillin.k.vk = KM_CORE_VKEY_BKSP;
return;
}

// TODO-LDML: error passthrough
std::cerr << "TODO-LDML: Error, unknown/unhandled action: " << action << std::endl;
std::cerr << "TODO-LDML: Error, unknown/unhandled action: " << type << std::endl;
fillin.type = LDML_ACTION_DONE;
}

Expand Down Expand Up @@ -711,7 +713,9 @@ int LdmlJsonTestSourceFactory::load(const km::kbp::path &compiled, const km::kbp
auto info_author = data["/keyboardTest3/info/author"_json_pointer].get<std::string>();
auto info_name = data["/keyboardTest3/info/name"_json_pointer].get<std::string>();
// TODO-LDML: store these elsewhere?
std::cout << "JSON: reading " << info_name << " test of " << info_keyboard << " by " << info_author << std::endl;
std::wcout << console_color::fg(console_color::BLUE) << "test file = " << path.name().c_str() << console_color::reset() << std::endl;
std::wcout << console_color::fg(console_color::YELLOW) << info_name.c_str() << "/ " << console_color::reset()
<< " test: " << info_keyboard.c_str() << " author: " << info_author.c_str() << std::endl;

auto all_tests = data["/keyboardTest3/tests"_json_pointer];
assert_or_return((!all_tests.empty()) && (all_tests.size() > 0)); // TODO-LDML: can be empty if repertoire only?
Expand All @@ -722,7 +726,7 @@ int LdmlJsonTestSourceFactory::load(const km::kbp::path &compiled, const km::kbp
auto test_name = test["/name"_json_pointer].get<std::string>();
std::string test_path;
test_path.append(info_name).append("/tests/").append(tests_name).append("/").append(test_name);
std::cout << "JSON: reading " << info_name << "/" << test_path << std::endl;
// std::cout << "JSON: reading " << info_name << "/" << test_path << std::endl;

std::unique_ptr<LdmlJsonTestSource> subtest(new LdmlJsonTestSource(test_path, kmxplus.get()));
assert_or_return(subtest->load(test) == 0);
Expand Down
40 changes: 16 additions & 24 deletions developer/src/kmc-ldml/test/fixtures/test-fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,44 +29,36 @@
},
"actions": [
{
"keystroke": {
"key": "s"
}
"type": "keystroke",
"key": "s"
},
{
"check": {
"result": "abc\\u0022...s"
}
"type": "check",
"result": "abc\\u0022...s"
},
{
"keystroke": {
"key": "t"
}
"type": "keystroke",
"key": "t"
},
{
"check": {
"result": "abc\\u0022...st"
}
"type": "check",
"result": "abc\\u0022...st"
},
{
"keystroke": {
"key": "u"
}
"type": "keystroke",
"key": "u"
},
{
"check": {
"result": "abc\\u0022...stu"
}
"type": "check",
"result": "abc\\u0022...stu"
},
{
"emit": {
"to": "v"
}
"type": "emit",
"to": "v"
},
{
"check": {
"result": "abc\\u0022...stuv"
}
"type": "check",
"result": "abc\\u0022...stuv"
}
]
}
Expand Down