Merge pull request #15923 from keymanapp/chore/core/15913-unit-test-follow-on
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled

chore(core): additional cleanup of unit tests 🎼
This commit is contained in:
Marc Durdin 2026-05-08 21:48:01 +10:00 committed by GitHub
commit b2d4cf98de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 1217 additions and 1387 deletions

View file

@ -3,6 +3,9 @@
*
* Keyman Core - Shared test library to load .kmn test rules for automated
* testing of .kmn keyboards
*
* Note that this file is superficially similar to ldml_test_source.cpp, but the
* parsing and use diverges far enough that they cannot be easily merged.
*/
#include <algorithm>

View file

@ -15,73 +15,26 @@
#include "context.hpp"
#include "../helpers/core_test_helpers.h"
#include "./actions_test_data.h"
struct TestData {
const char* test_name;
/**
* the app context stored in the state, _before_ transform is applied -- NFU
*/
const km_core_cu *initial_app_context;
/**
* cached context _after_ actions have been applied -- guaranteed NFD
* (essentially, this is initial_cached_context -
* actions_code_points_to_delete + actions_output) - no markers supported
*/
const km_core_cu *final_cached_context;
/**
* number of NFD code points that the keyboard processor has asked to remove
* in its actions
*/
int actions_code_points_to_delete;
/**
* NFD string that the keyboard processor has asked to insert in its actions
*/
const std::u32string actions_output;
/**
* expected: NFU code points to ask app to remove
*/
const unsigned int expected_delete;
/**
* expected: adjusted NFC output to insert into the app
*/
const std::u32string expected_output;
/**
* expected: NFU adjusted final app context, which will be NFC from the
* boundary of the transform, but will not have been modified prior to that.
* Should match char-for-char what the app ends up with in its text buffer.
*/
const km_core_cu *expected_final_app_context;
/**
* expected: the characters deleted from the context
*/
const std::u32string expected_deleted_context;
};
std::string GenerateTestName(const testing::TestParamInfo<TestData>& info) {
return info.param.test_name;
}
class GetActionApiTest : public testing::TestWithParam<TestData> {
class GetActionApiTest : public testing::TestWithParam<ActionsTestData> {
protected:
km_core_keyboard * test_kb = nullptr;
km_core_state * test_state = nullptr;
km_core_actions * test_actions = nullptr;
void Initialize(TestData const& data) {
void Initialize(ActionsTestData const& data) {
if(data.final_cached_context_string == nullptr) {
// We skip the shared tests that don't have a input context string
return;
}
km::core::path path = km::core::path::join(test_dir, "..", "ldml", "fixtures", "keyboards", "17.0", "k_001_tiny.kmx");
auto blob = km::tests::load_kmx_file(path.native().c_str());
ASSERT_STATUS_OK(km_core_keyboard_load_from_blob(path.stem().c_str(), blob.data(), blob.size(), &test_kb));
ASSERT_STATUS_OK(km_core_state_create(test_kb, test_empty_env_opts, &test_state));
ASSERT_STATUS_OK(set_context_from_string(km_core_state_context(test_state), data.final_cached_context));
ASSERT_STATUS_OK(set_context_from_string(km_core_state_context(test_state), data.final_cached_context_string));
ASSERT_STATUS_OK(set_context_from_string(km_core_state_app_context(test_state), data.initial_app_context));
test_actions = new km_core_actions;
@ -115,6 +68,7 @@ protected:
}
if(test_actions) {
delete [] test_actions->output;
delete [] test_actions->persist_options;
delete test_actions;
test_actions = nullptr;
}
@ -132,6 +86,11 @@ TEST_P(GetActionApiTest, TestActionsApi) {
auto data = GetParam();
ASSERT_NO_FATAL_FAILURE(Initialize(data));
if(data.final_cached_context_string == nullptr) {
GTEST_SKIP() << "Skip the shared tests that don't have a input context string";
return;
}
auto actual_actions = km_core_state_get_actions(test_state);
std::cout << " (" << data.test_name << "): delete: " << data.expected_delete << " output: |" << std::u32string(actual_actions->output) << "|" << std::endl;
@ -160,184 +119,6 @@ TEST_P(GetActionApiTest, TestActionsApi) {
delete [] actual_final_app_context;
}
const TestData values[] = {
// Null boundary tests
// Note that .final_cached_context_items is not used in these tests
{
"Noop",
/* app context pre transform: */ u"",
/* cached context post transform: */ u"",
/* action del, output: */ 0, U"",
// ---- results ----
/* action del, output: */ 0, U"",
/* app_context: */ u"",
/* expected del */ U""
},
{
"NoOutput",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abc",
/* action del, output: */ 0, U"",
// ---- results ----
/* action del, output: */ 0, U"",
/* app_context: */ u"abc",
/* expected del */ U""
},
{
"NoContext",
/* app context pre transform: */ u"",
/* cached context post transform: */ u"def",
/* action del, output: */ 0, U"def",
// ---- results ----
/* action del, output: */ 0, U"def",
/* app_context: */ u"def",
/* expected del */ U""
},
// Simple tests -- no deletions involved
{
"NoNormalization",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abcdef",
/* action del, output: */ 0, U"def",
// ---- results ----
/* action del, output: */ 0, U"def",
/* app_context: */ u"abcdef",
/* expected del */ U""
},
{
"OutputToNfcBasic",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abcde\u0300f",
/* action del, output: */ 0, U"de\u0300f",
// ---- results ----
/* action del, output: */ 0, U"dèf",
/* app_context: */ u"abcdèf",
/* expected del */ U""
},
{
"OutputToNfcHefty",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abcA\u0300" u"e\u0316\u0301" u"\u0073\u0323\u0307" u"\u0041\u030a" u"\U000114B9\U000114B0",
/* action del, output: */ 0, U"A\u0300" U"e\u0316\u0301" U"\u0073\u0323\u0307" U"\u0041\u030a" U"\U000114B9\U000114B0",
// ---- results ----
/* action del, output: */ 0, U"À" U"é̖" U"\u1e69" U"\u00c5" U"\U000114BC",
/* app_context: */ u"abcÀé̖\u1e69\u00c5\U000114BC",
/* expected del */ U""
},
// Interaction with input context when not on normalization boundary
{
"BacktrackOneCharacterToCombineAsNfc",
/* app context pre transform: */ u"XYZA",
/* cached context post transform: */ u"XYZA\u0300abc",
/* action del, output: */ 0, U"\u0300abc",
// ---- results ----
/* action del, output: */ 1, U"Àabc",
/* app_context: */ u"XYZÀabc",
/* expected del */ U"A"
},
{
"BacktrackECombCirc2CharsToCombineAsNfc",
/* app context pre transform: */ u"abce\u0302",
/* cached context post transform: */ u"abce\u0323\u0302",
/* action del, output: */ 1, U"\u0323\u0302",
// ---- results ----
/* action del, output: */ 2, U"",
/* app_context: */ u"abcệ",
/* expected del */ U"e\u0302"
},
{
"OneBackspaceForNfdConvertsIntoOneCharInNfcAndRecombine",
/* app context pre transform: */ u"abcê",
/* cached context post transform: */ u"abce\u0323\u0302",
/* action del, output: */ 1, U"\u0323\u0302", // NFD input; delete 1: \u0302
// ---- results ----
/* action del, output: */ 1, U"", // NFC output; delete 1: ê
/* app_context: */ u"abcệ",
/* expected del */ U"ê"
},
// a\u0300 should not be normalized because it is not otherwise impacted by
// the action.
{
"AvoidEditingTooFarBackInContextWhenFindingNormalizationBoundary",
/* app context pre transform: */ u"a\u0300bcê",
/* cached context post transform: */ u"a\u0300bce\u0323\u0302",
/* action del, output: */ 1, U"\u0323\u0302", // NFD input; delete 1: \u0302
// ---- results ----
/* action del, output: */ 1, U"", // NFC output; delete 1: ê
/* app_context: */ u"a\u0300bcệ",
/* expected del */ U"ê"
},
// If we don't reach a normalization boundary, we still should continue to work
{
"NormalizableLettersAtStartOfContext",
/* app context pre transform: */ u"\u0300",
/* cached context post transform: */ u"\u0323\u0300\u0302",
/* action del, output: */ 1, U"\u0323\u0300\u0302", // NFD input;
// ---- results ----
/* action del, output: */ 1, U"\u0323\u0300\u0302", // NFC output is still decomposed because there is no base
/* app_context: */ u"\u0323\u0300\u0302",
/* expected del */ U"\u0300"
},
// Modifies the base as well as diacritic
{
"TwoBackspacesForNfdConvertsIntoOneCharInNfcAndRecombine",
/* app context pre transform: */ u"abcê",
/* cached context post transform: */ u"abca\u0323\u0302",
/* action del, output: */ 2, U"a\u0323\u0302", // NFD input; delete 2: e\u0302
// ---- results ----
/* action del, output: */ 1, U"", // NFC output; delete 1: ê
/* app_context: */ u"abcậ",
/* expected del */ U"ê"
},
// surrogate pair tests
{
"SurrogatePairInContext",
/* app context pre transform: */ u"abc\U0001F607ê",
/* cached context post transform: */ u"abc\U0001F607a\u0323\u0302",
/* action del, output: */ 2, U"a\u0323\u0302",
// ---- results ----
/* action del, output: */ 1, U"",
/* app_context: */ u"abc\U0001F607",
/* expected del */ U"ê"
},
{
"SurrogatePairInOutput",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abc\U0001F607",
/* action del, output: */ 0, U"\U0001F607",
// ---- results ----
/* action del, output: */ 0, U"\U0001F607",
/* app_context: */ u"abc\U0001F607",
/* expected del */ U""
},
{
"SurrogatePairsInBothContextAndOutput",
/* app context pre transform: */ u"a\U0001F607bcê",
/* cached context post transform: */ u"a\U0001F607bca\U0001F60E",
/* action del, output: */ 2, U"a\U0001F60E",
// ---- results ----
/* action del, output: */ 1, U"a\U0001F60E",
/* app_context: */ u"a\U0001F607bca\U0001F60E",
/* expected del */ U"ê"
}
};
INSTANTIATE_TEST_SUITE_P(KeymanCore, GetActionApiTest, testing::ValuesIn(values), GenerateTestName);
INSTANTIATE_TEST_SUITE_P(KeymanCore, GetActionApiTest, testing::ValuesIn(actionsTestData), GenerateTestName);

View file

@ -15,68 +15,15 @@
#include "context.hpp"
#include "../helpers/core_test_helpers.h"
#include "./actions_test_data.h"
struct TestData {
const char* test_name;
/**
* the app context stored in the state, _before_ transform is applied -- NFU
*/
const km_core_cu *initial_app_context;
/**
* cached context _after_ actions have been applied -- guaranteed NFD
* (essentially, this is initial_cached_context -
* actions_code_points_to_delete + actions_output) - no markers supported
*/
const km_core_cu *final_cached_context_string;
/**
* cached context _after_ actions have been applied -- guaranteed NFD
* (essentially, this is initial_cached_context -
* actions_code_points_to_delete + actions_output) - markers supported
*/
const km_core_context_item *final_cached_context_items;
/**
* number of NFD code points that the keyboard processor has asked to remove in its actions
*/
int actions_code_points_to_delete;
/**
* NFD string that the keyboard processor has asked to insert in its actions
*/
const std::u32string actions_output;
/**
* expected: NFU code points to ask app to remove
*/
const unsigned int expected_delete;
/**
* expected: adjusted NFC output to insert into the app
*/
const std::u32string expected_output;
/**
* expected: NFU adjusted final app context, which will be NFC from the
* boundary of the transform, but will not have been modified prior to that.
* Should match char-for-char what the app ends up with in its text buffer.
*/
const km_core_cu *expected_final_app_context;
};
std::string GenerateTestName(const testing::TestParamInfo<TestData>& info) {
return info.param.test_name;
}
class ActionsNormalizeApiTest : public testing::TestWithParam<TestData> {
class ActionsNormalizeApiTest : public testing::TestWithParam<ActionsTestData> {
protected:
km_core_keyboard * test_kb = nullptr;
km_core_state * test_state = nullptr;
km_core_actions test_actions = {0};
void Initialize(TestData const& data) {
void Initialize(ActionsTestData const& data) {
km::core::path path = km::core::path::join(test_dir, "..", "ldml", "fixtures", "keyboards", "17.0", "k_001_tiny.kmx");
auto blob = km::tests::load_kmx_file(path.native().c_str());
ASSERT_STATUS_OK(km_core_keyboard_load_from_blob(path.stem().c_str(), blob.data(), blob.size(), &test_kb));
@ -143,271 +90,4 @@ TEST_P(ActionsNormalizeApiTest, TestActionsNormalize) {
ASSERT_NO_FATAL_FAILURE(km::tests::compare_context(km_core_state_app_context(test_state), data.expected_final_app_context));
}
const km_core_context_item items_1[] = { //u"a\U0001F607b\uFFFF\u0008\u0001ca\U0001F60E",
{ KM_CORE_CT_CHAR, {0,}, { 0x0061 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x1F607 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x0062 } },
{ KM_CORE_CT_MARKER, {0,}, { 0x1 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x0063 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x0061 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x1F60E } },
KM_CORE_CONTEXT_ITEM_END
};
const km_core_context_item items_2[] = { //u"a\U0001F607bca\U0001F60E\uFFFF\u0008\u0001",
{ KM_CORE_CT_CHAR, {0,}, { 0x0061 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x1F607 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x0062 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x0063 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x0061 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x1F60E } },
{ KM_CORE_CT_MARKER, {0,}, { 0x1 } },
KM_CORE_CONTEXT_ITEM_END
};
const km_core_context_item items_11067[] = {
{ KM_CORE_CT_CHAR, {0,}, { U'𐒻' } },
{ KM_CORE_CT_CHAR, {0,}, { U'𐒷' } },
KM_CORE_CONTEXT_ITEM_END
};
const TestData values[] = {
// Null boundary tests
{
"Noop",
/* app context pre transform: */ u"",
/* cached context post transform: */ u"",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"",
// ---- results ----
/* action del, output: */ 0, U"",
/* app_context: */ u""
},
{
"NoOutput",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abc",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"",
// ---- results ----
/* action del, output: */ 0, U"",
/* app_context: */ u"abc"
},
{
"NoContext",
/* app context pre transform: */ u"",
/* cached context post transform: */ u"def",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"def",
// ---- results ----
/* action del, output: */ 0, U"def",
/* app_context: */ u"def"
},
// Simple tests -- no deletions involved
{
"NoNormalization",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abcdef",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"def",
// ---- results ----
/* action del, output: */ 0, U"def",
/* app_context: */ u"abcdef"
},
{
"OutputToNfcBasic",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abcde\u0300f",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"de\u0300f",
// ---- results ----
/* action del, output: */ 0, U"dèf",
/* app_context: */ u"abcdèf"
},
{
"OutputToNfcHefty",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abcA\u0300" u"e\u0316\u0301" u"\u0073\u0323\u0307" u"\u0041\u030a" u"\U000114B9\U000114B0",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"A\u0300" U"e\u0316\u0301" U"\u0073\u0323\u0307" U"\u0041\u030a" U"\U000114B9\U000114B0",
// ---- results ----
/* action del, output: */ 0, U"À" U"é̖" U"\u1e69" U"\u00c5" U"\U000114BC",
/* app_context: */ u"abcÀé̖\u1e69\u00c5\U000114BC"
},
// Interaction with input context when not on normalization boundary
{
"BacktrackOneCharacterToCombineAsNfc",
/* app context pre transform: */ u"XYZA",
/* cached context post transform: */ u"XYZA\u0300abc",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"\u0300abc",
// ---- results ----
/* action del, output: */ 1, U"Àabc",
/* app_context: */ u"XYZÀabc"
},
{
"BacktrackEcombCirc2CharsToCombineAsNfc",
/* app context pre transform: */ u"abce\u0302",
/* cached context post transform: */ u"abce\u0323\u0302",
/* cached context post transform: */ nullptr,
/* action del, output: */ 1, U"\u0323\u0302",
// ---- results ----
/* action del, output: */ 2, U"",
/* app_context: */ u"abcệ"
},
{
"OneBackspaceToDeleteLastNfdCharacter15487",
/* app context pre transform: */ u"abcê", // NFC
/* cached context post transform: */ u"abce",
/* cached context post transform: */ nullptr,
/* action del, output: */ 1, U"", // NFD input; delete 1: \u0302
// ---- results ----
/* action del, output: */ 1, U"e", // NFC output; delete 1: e
/* app_context: */ u"abce"
},
{
"OneBackspaceForNfdConvertsIntoOneCharInNfcAndRecombine",
/* app context pre transform: */ u"abcê",
/* cached context post transform: */ u"abce\u0323\u0302",
/* cached context post transform: */ nullptr,
/* action del, output: */ 1, U"\u0323\u0302", // NFD input; delete 1: \u0302
// ---- results ----
/* action del, output: */ 1, U"", // NFC output; delete 1: ê
/* app_context: */ u"abcệ"
},
// a\u0300 should not be normalized because it is not otherwise impacted by
// the action.
{
"AvoidEditingTooFarBackInContextWhenFindingNormalizationBoundary",
/* app context pre transform: */ u"a\u0300bcê",
/* cached context post transform: */ u"a\u0300bce\u0323\u0302",
/* cached context post transform: */ nullptr,
/* action del, output: */ 1, U"\u0323\u0302", // NFD input; delete 1: \u0302
// ---- results ----
/* action del, output: */ 1, U"", // NFC output; delete 1: ê
/* app_context: */ u"a\u0300bcệ"
},
// If we don't reach a normalization boundary, we still should continue to work
{
"NormalizableLettersAtStartOfContext",
/* app context pre transform: */ u"\u0300",
/* cached context post transform: */ u"\u0323\u0300\u0302",
/* cached context post transform: */ nullptr,
/* action del, output: */ 1, U"\u0323\u0300\u0302", // NFD input;
// ---- results ----
/* action del, output: */ 1, U"\u0323\u0300\u0302", // NFC output is still decomposed because there is no base
/* app_context: */ u"\u0323\u0300\u0302"
},
// #15505 - normalization of Bengali characters
{
"BengaliNormalizationOfU09C7U09D7U09CC",
/* app context pre transform: */ u"\u0995\u09C7",
/* cached context post transform: */ u"\u0995\u09C7\u09D7",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"\u09D7",
// ---- results ----
/* action del, output: */ 1, U"\u09CC",
/* app_context: */ u"\u0995\u09CC"
},
// Modifies the base as well as diacritic
{
"TwoBackspacesForNfdConvertsIntoOneCharInNfcAndRecombine",
/* app context pre transform: */ u"abcê",
/* cached context post transform: */ u"abca\u0323\u0302",
/* cached context post transform: */ nullptr,
/* action del, output: */ 2, U"a\u0323\u0302", // NFD input; delete 2: e\u0302
// ---- results ----
/* action del, output: */ 1, U"", // NFC output; delete 1: ê
/* app_context: */ u"abcậ"
},
// surrogate pair tests
{
"SurrogatePairInContext",
/* app context pre transform: */ u"abc\U0001F607ê",
/* cached context post transform: */ u"abc\U0001F607a\u0323\u0302",
/* cached context post transform: */ nullptr,
/* action del, output: */ 2, U"a\u0323\u0302",
// ---- results ----
/* action del, output: */ 1, U"",
/* app_context: */ u"abc\U0001F607"
},
{
"SurrogatePairInOutput",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abc\U0001F607",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"\U0001F607",
// ---- results ----
/* action del, output: */ 0, U"\U0001F607",
/* app_context: */ u"abc\U0001F607"
},
{
"SurrogatePairsInBothContextAndOutput",
/* app context pre transform: */ u"a\U0001F607bcê",
/* cached context post transform: */ u"a\U0001F607bca\U0001F60E",
/* cached context post transform: */ nullptr,
/* action del, output: */ 2, U"a\U0001F60E",
// ---- results ----
/* action del, output: */ 1, U"a\U0001F60E",
/* app_context: */ u"a\U0001F607bca\U0001F60E"
},
// Marker tests
{
"AMarkerInTheCachedContextShouldNotShowUpInAppContext",
/* app context pre transform: */ u"a\U0001F607bcê",
/* cached context post transform: */ nullptr,
/* cached context post transform: */ &items_1[0],
/* action del, output: */ 2, U"a\U0001F60E",
// ---- results ----
/* action del, output: */ 1, U"a\U0001F60E",
/* app_context: */ u"a\U0001F607bca\U0001F60E"
},
{
"AMarkerInTheModifiedSectionOfCachedContextShouldNotShowUpInAppContext",
/* app context pre transform: */ u"a\U0001F607bcê",
/* cached context post transform: */ nullptr,
/* cached context post transform: */ &items_2[0],
/* action del, output: */ 2, U"a\U0001F60E",
// ---- results ----
/* action del, output: */ 1, U"a\U0001F60E",
/* app_context: */ u"a\U0001F607bca\U0001F60E"
},
// regression #11067
{
"ANonBmpCharInContext11067",
/* app context pre transform: */ u"𐒻",
/* cached context post transform: */ u"𐒻𐒷",
/* cached context post transform: */ &items_11067[0],
/* action del, output: */ 0, U"𐒻𐒷",
// ---- results ----
/* action del, output: */ 1, U"𐒻𐒷",
/* app_context: */ u"𐒻𐒷"
}
};
INSTANTIATE_TEST_SUITE_P(KeymanCore, ActionsNormalizeApiTest, testing::ValuesIn(values), GenerateTestName);
INSTANTIATE_TEST_SUITE_P(KeymanCore, ActionsNormalizeApiTest, testing::ValuesIn(actionsTestData), GenerateTestName);

View file

@ -0,0 +1,306 @@
#include "./actions_test_data.h"
const km_core_context_item items_1[] = { //u"a\U0001F607b\uFFFF\u0008\u0001ca\U0001F60E",
{ KM_CORE_CT_CHAR, {0,}, { 0x0061 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x1F607 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x0062 } },
{ KM_CORE_CT_MARKER, {0,}, { 0x1 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x0063 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x0061 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x1F60E } },
KM_CORE_CONTEXT_ITEM_END
};
const km_core_context_item items_2[] = { //u"a\U0001F607bca\U0001F60E\uFFFF\u0008\u0001",
{ KM_CORE_CT_CHAR, {0,}, { 0x0061 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x1F607 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x0062 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x0063 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x0061 } },
{ KM_CORE_CT_CHAR, {0,}, { 0x1F60E } },
{ KM_CORE_CT_MARKER, {0,}, { 0x1 } },
KM_CORE_CONTEXT_ITEM_END
};
const km_core_context_item items_11067[] = {
{ KM_CORE_CT_CHAR, {0,}, { U'𐒻' } },
{ KM_CORE_CT_CHAR, {0,}, { U'𐒷' } },
KM_CORE_CONTEXT_ITEM_END
};
const std::vector<ActionsTestData> actionsTestData = {
// Null boundary tests
{
"Noop",
/* app context pre transform: */ u"",
/* cached context post transform: */ u"",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"",
// ---- results ----
/* expected action del, output: */ 0, U"",
/* expected app_context: */ u"",
/* expected del */ U""
},
{
"NoOutput",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abc",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"",
// ---- results ----
/* expected action del, output: */ 0, U"",
/* expected app_context: */ u"abc",
/* expected del */ U""
},
{
"NoContext",
/* app context pre transform: */ u"",
/* cached context post transform: */ u"def",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"def",
// ---- results ----
/* expected action del, output: */ 0, U"def",
/* expected app_context: */ u"def",
/* expected del */ U""
},
// Simple tests -- no deletions involved
{
"NoNormalization",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abcdef",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"def",
// ---- results ----
/* expected action del, output: */ 0, U"def",
/* expected app_context: */ u"abcdef",
/* expected del */ U""
},
{
"OutputToNfcBasic",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abcde\u0300f",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"de\u0300f",
// ---- results ----
/* expected action del, output: */ 0, U"dèf",
/* expected app_context: */ u"abcdèf",
/* expected del */ U""
},
{
"OutputToNfcHefty",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abcA\u0300" u"e\u0316\u0301" u"\u0073\u0323\u0307" u"\u0041\u030a" u"\U000114B9\U000114B0",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"A\u0300" U"e\u0316\u0301" U"\u0073\u0323\u0307" U"\u0041\u030a" U"\U000114B9\U000114B0",
// ---- results ----
/* expected action del, output: */ 0, U"À" U"é̖" U"\u1e69" U"\u00c5" U"\U000114BC",
/* expected app_context: */ u"abcÀé̖\u1e69\u00c5\U000114BC",
/* expected del */ U""
},
// Interaction with input context when not on normalization boundary
{
"BacktrackOneCharacterToCombineAsNfc",
/* app context pre transform: */ u"XYZA",
/* cached context post transform: */ u"XYZA\u0300abc",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"\u0300abc",
// ---- results ----
/* expected action del, output: */ 1, U"Àabc",
/* expected app_context: */ u"XYZÀabc",
/* expected del */ U"A"
},
{
"BacktrackECombCirc2CharsToCombineAsNfc",
/* app context pre transform: */ u"abce\u0302",
/* cached context post transform: */ u"abce\u0323\u0302",
/* cached context post transform: */ nullptr,
/* action del, output: */ 1, U"\u0323\u0302",
// ---- results ----
/* expected action del, output: */ 2, U"",
/* expected app_context: */ u"abcệ",
/* expected del */ U"e\u0302"
},
{
"OneBackspaceToDeleteLastNfdCharacter15487",
/* app context pre transform: */ u"abcê", // NFC
/* cached context post transform: */ u"abce",
/* cached context post transform: */ nullptr,
/* action del, output: */ 1, U"", // NFD input; delete 1: \u0302
// ---- results ----
/* expected action del, output: */ 1, U"e", // NFC output; delete 1: e
/* expected app_context: */ u"abce",
/* expected del */ U"ê"
},
{
"OneBackspaceToDeleteLastNfdCharacterWithNfdAppContext15487",
/* app context pre transform: */ u"abce\u0302", // NFD
/* cached context post transform: */ u"abce",
/* cached context post transform: */ nullptr,
/* action del, output: */ 1, U"", // NFD input; delete 1: \u0302
// ---- results ----
/* expected action del, output: */ 1, U"", // NFC output; delete 1: e
/* expected app_context: */ u"abce",
/* expected del */ U"\u0302"
},
{
"OneBackspaceForNfdConvertsIntoOneCharInNfcAndRecombine",
/* app context pre transform: */ u"abcê",
/* cached context post transform: */ u"abce\u0323\u0302",
/* cached context post transform: */ nullptr,
/* action del, output: */ 1, U"\u0323\u0302", // NFD input; delete 1: \u0302
// ---- results ----
/* expected action del, output: */ 1, U"", // NFC output; delete 1: ê
/* expected app_context: */ u"abcệ",
/* expected del */ U"ê"
},
// a\u0300 should not be normalized because it is not otherwise impacted by
// the action.
{
"AvoidEditingTooFarBackInContextWhenFindingNormalizationBoundary",
/* app context pre transform: */ u"a\u0300bcê",
/* cached context post transform: */ u"a\u0300bce\u0323\u0302",
/* cached context post transform: */ nullptr,
/* action del, output: */ 1, U"\u0323\u0302", // NFD input; delete 1: \u0302
// ---- results ----
/* expected action del, output: */ 1, U"", // NFC output; delete 1: ê
/* expected app_context: */ u"a\u0300bcệ",
/* expected del */ U"ê"
},
// If we don't reach a normalization boundary, we still should continue to work
{
"NormalizableLettersAtStartOfContext",
/* app context pre transform: */ u"\u0300",
/* cached context post transform: */ u"\u0323\u0300\u0302",
/* cached context post transform: */ nullptr,
/* action del, output: */ 1, U"\u0323\u0300\u0302", // NFD input;
// ---- results ----
/* expected action del, output: */ 1, U"\u0323\u0300\u0302", // NFC output is still decomposed because there is no base
/* expected app_context: */ u"\u0323\u0300\u0302",
/* expected del */ U"\u0300"
},
// #15505 - normalization of Bengali characters
{
"BengaliNormalizationOfU09C7U09D7U09CC",
/* app context pre transform: */ u"\u0995\u09C7",
/* cached context post transform: */ u"\u0995\u09C7\u09D7",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"\u09D7",
// ---- results ----
/* expected action del, output: */ 1, U"\u09CC",
/* expected app_context: */ u"\u0995\u09CC",
/* expected del */ U"\u09C7"
},
// Modifies the base as well as diacritic
{
"TwoBackspacesForNfdConvertsIntoOneCharInNfcAndRecombine",
/* app context pre transform: */ u"abcê",
/* cached context post transform: */ u"abca\u0323\u0302",
/* cached context post transform: */ nullptr,
/* action del, output: */ 2, U"a\u0323\u0302", // NFD input; delete 2: e\u0302
// ---- results ----
/* expected action del, output: */ 1, U"", // NFC output; delete 1: ê
/* expected app_context: */ u"abcậ",
/* expected del */ U"ê"
},
// surrogate pair tests
{
"SurrogatePairInContext",
/* app context pre transform: */ u"abc\U0001F607ê",
/* cached context post transform: */ u"abc\U0001F607a\u0323\u0302",
/* cached context post transform: */ nullptr,
/* action del, output: */ 2, U"a\u0323\u0302",
// ---- results ----
/* expected action del, output: */ 1, U"",
/* expected app_context: */ u"abc\U0001F607",
/* expected del */ U"ê"
},
{
"SurrogatePairInOutput",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abc\U0001F607",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"\U0001F607",
// ---- results ----
/* expected action del, output: */ 0, U"\U0001F607",
/* expected app_context: */ u"abc\U0001F607",
/* expected del */ U""
},
{
"SurrogatePairsInBothContextAndOutput",
/* app context pre transform: */ u"a\U0001F607bcê",
/* cached context post transform: */ u"a\U0001F607bca\U0001F60E",
/* cached context post transform: */ nullptr,
/* action del, output: */ 2, U"a\U0001F60E",
// ---- results ----
/* expected action del, output: */ 1, U"a\U0001F60E",
/* expected app_context: */ u"a\U0001F607bca\U0001F60E",
/* expected del */ U"ê"
},
// Marker tests
{
"AMarkerInTheCachedContextShouldNotShowUpInAppContext",
/* app context pre transform: */ u"a\U0001F607bcê",
/* cached context post transform: */ nullptr,
/* cached context post transform: */ &items_1[0],
/* action del, output: */ 2, U"a\U0001F60E",
// ---- results ----
/* expected action del, output: */ 1, U"a\U0001F60E",
/* expected app_context: */ u"a\U0001F607bca\U0001F60E",
/* expected del */ U"ê"
},
{
"AMarkerInTheModifiedSectionOfCachedContextShouldNotShowUpInAppContext",
/* app context pre transform: */ u"a\U0001F607bcê",
/* cached context post transform: */ nullptr,
/* cached context post transform: */ &items_2[0],
/* action del, output: */ 2, U"a\U0001F60E",
// ---- results ----
/* expected action del, output: */ 1, U"a\U0001F60E",
/* expected app_context: */ u"a\U0001F607bca\U0001F60E",
/* expected del */ U"ê"
},
// regression #11067
{
"ANonBmpCharInContext11067",
/* app context pre transform: */ u"𐒻",
/* cached context post transform: */ u"𐒻𐒷",
/* cached context post transform: */ &items_11067[0],
/* action del, output: */ 0, U"𐒻𐒷",
// ---- results ----
/* expected action del, output: */ 1, U"𐒻𐒷",
/* expected app_context: */ u"𐒻𐒷",
/* expected del: */ U"\x104BB"
}
};
std::string GenerateTestName(const testing::TestParamInfo<ActionsTestData>& info) {
return info.param.test_name;
}

View file

@ -0,0 +1,64 @@
#pragma once
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include "keyman_core.h"
struct ActionsTestData {
const char* test_name;
/**
* the app context stored in the state, _before_ transform is applied -- NFU
*/
const km_core_cu *initial_app_context;
/**
* cached context _after_ actions have been applied -- guaranteed NFD
* (essentially, this is initial_cached_context -
* actions_code_points_to_delete + actions_output) - no markers supported
*/
const km_core_cu *final_cached_context_string;
/**
* cached context _after_ actions have been applied -- guaranteed NFD
* (essentially, this is initial_cached_context -
* actions_code_points_to_delete + actions_output) - markers supported
*/
const km_core_context_item *final_cached_context_items;
/**
* number of NFD code points that the keyboard processor has asked to remove in its actions
*/
int actions_code_points_to_delete;
/**
* NFD string that the keyboard processor has asked to insert in its actions
*/
const std::u32string actions_output;
/**
* expected: NFU code points to ask app to remove
*/
const unsigned int expected_delete;
/**
* expected: adjusted NFC output to insert into the app
*/
const std::u32string expected_output;
/**
* expected: NFU adjusted final app context, which will be NFC from the
* boundary of the transform, but will not have been modified prior to that.
* Should match char-for-char what the app ends up with in its text buffer.
*/
const km_core_cu *expected_final_app_context;
/**
* expected: the characters deleted from the context
*/
const std::u32string expected_deleted_context;
};
extern const std::vector<ActionsTestData> actionsTestData;
std::string GenerateTestName(const testing::TestParamInfo<ActionsTestData>& info);

View file

@ -15,72 +15,15 @@
#include "context.hpp"
#include "../helpers/core_test_helpers.h"
#include "./actions_test_data.h"
// TODO-WEB-CORE: merge with actions_normalize.tests.cpp? These are identical;
// note that actions_get_api and actions_set_api data are subtly different,
// opportunity to merge those too?
struct TestData {
const char* test_name;
/**
* the app context stored in the state, _before_ transform is applied -- NFU
*/
const km_core_cu *initial_app_context;
/**
* cached context _after_ actions have been applied -- guaranteed NFD
* (essentially, this is initial_cached_context -
* actions_code_points_to_delete + actions_output) - no markers supported
*/
const km_core_cu *final_cached_context_string;
/**
* cached context _after_ actions have been applied -- guaranteed NFD
* (essentially, this is initial_cached_context -
* actions_code_points_to_delete + actions_output) - markers supported
*/
const km_core_context_item *final_cached_context_items;
/**
* number of NFD code points that the keyboard processor has asked to remove in its actions
*/
int actions_code_points_to_delete;
/**
* NFD string that the keyboard processor has asked to insert in its actions
*/
const std::u32string actions_output;
/**
* expected: NFU code points to ask app to remove
*/
const unsigned int expected_delete;
/**
* expected: adjusted NFC output to insert into the app
*/
const std::u32string expected_output;
/**
* expected: NFU adjusted final app context, which will be NFC from the
* boundary of the transform, but will not have been modified prior to that.
* Should match char-for-char what the app ends up with in its text buffer.
*/
const km_core_cu *expected_final_app_context;
};
std::string GenerateTestName(const testing::TestParamInfo<TestData>& info) {
return info.param.test_name;
}
class ActionsUpdateAppContextNfuApiTest : public testing::TestWithParam<TestData> {
class ActionsUpdateAppContextNfuApiTest : public testing::TestWithParam<ActionsTestData> {
protected:
km_core_keyboard * test_kb = nullptr;
km_core_state * test_state = nullptr;
km_core_actions test_actions = {0};
void Initialize(TestData const& data) {
void Initialize(ActionsTestData const& data) {
km::core::path path = km::core::path::join(test_dir, "..", "ldml", "fixtures", "keyboards", "17.0", "k_001_tiny.kmx");
auto blob = km::tests::load_kmx_file(path.native().c_str());
ASSERT_STATUS_OK(km_core_keyboard_load_from_blob(path.stem().c_str(), blob.data(), blob.size(), &test_kb));
@ -172,7 +115,23 @@ const km_core_context_item items_2[] = { //u"a\U0001F607bca\U0001F60E\uFFFF\u000
KM_CORE_CONTEXT_ITEM_END
};
const TestData values[] = {
const km_core_context_item items_11067[] = {
{ KM_CORE_CT_CHAR, {0,}, { U'𐒻' } },
{ KM_CORE_CT_CHAR, {0,}, { U'𐒷' } },
KM_CORE_CONTEXT_ITEM_END
};
/**
* Test cases for non-normalized app context update. These look superficially
* similar to the data in `actionsTestData`, but diverge when taking into
* account normalization of output. Several normalization-related tests in
* `actionsTestData` are therefore also currently excluded here.
*
* @todo: (low priority) `actionsTestData` and `actionsTestDataNFU` could be
* combined with additional entries for each NFU result.
*/
const std::vector<ActionsTestData> actionsTestDataNFU = {
// Null boundary tests
{
@ -182,8 +141,9 @@ const TestData values[] = {
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"",
// ---- results ----
/* action del, output: */ 0, U"",
/* app_context: */ u""
/* expected action del, output: */ 0, U"",
/* expected app_context: */ u"",
/* expected del */ U""
},
{
@ -193,8 +153,9 @@ const TestData values[] = {
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"",
// ---- results ----
/* action del, output: */ 0, U"",
/* app_context: */ u"abc"
/* expected action del, output: */ 0, U"",
/* expected app_context: */ u"abc",
/* expected del */ U""
},
{
@ -204,10 +165,26 @@ const TestData values[] = {
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"def",
// ---- results ----
/* action del, output: */ 0, U"def",
/* app_context: */ u"def"
/* expected action del, output: */ 0, U"def",
/* expected app_context: */ u"def",
/* expected del */ U""
},
// Simple tests -- no deletions involved
{
"NoNormalization",
/* app context pre transform: */ u"abc",
/* cached context post transform: */ u"abcdef",
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"def",
// ---- results ----
/* expected action del, output: */ 0, U"def",
/* expected app_context: */ u"abcdef",
/* expected del */ U""
},
// surrogate pair tests
{
@ -217,8 +194,9 @@ const TestData values[] = {
/* cached context post transform: */ nullptr,
/* action del, output: */ 1, U"a\u0323\u0302",
// ---- results ----
/* action del, output: */ 1, U"a\u0323\u0302",
/* app_context: */ u"abc\U0001F607a\u0323\u0302"
/* expected action del, output: */ 1, U"a\u0323\u0302",
/* expected app_context: */ u"abc\U0001F607a\u0323\u0302",
/* expected del */ U"ê"
},
{
@ -228,8 +206,9 @@ const TestData values[] = {
/* cached context post transform: */ nullptr,
/* action del, output: */ 0, U"\U0001F607",
// ---- results ----
/* action del, output: */ 0, U"\U0001F607",
/* app_context: */ u"abc\U0001F607"
/* expected action del, output: */ 0, U"\U0001F607",
/* expected app_context: */ u"abc\U0001F607",
/* expected del */ U""
},
{
@ -239,8 +218,9 @@ const TestData values[] = {
/* cached context post transform: */ nullptr,
/* action del, output: */ 1, U"a\U0001F60E",
// ---- results ----
/* action del, output: */ 1, U"a\U0001F60E",
/* app_context: */ u"a\U0001F607bca\U0001F60E"
/* expected action del, output: */ 1, U"a\U0001F60E",
/* expected app_context: */ u"a\U0001F607bca\U0001F60E",
/* expected del */ U"ê"
},
// Marker tests
@ -252,8 +232,9 @@ const TestData values[] = {
/* cached context post transform: */ &items_1[0],
/* action del, output: */ 1, U"a\U0001F60E",
// ---- results ----
/* action del, output: */ 1, U"a\U0001F60E",
/* app_context: */ u"a\U0001F607bca\U0001F60E"
/* expected action del, output: */ 1, U"a\U0001F60E",
/* expected app_context: */ u"a\U0001F607bca\U0001F60E",
/* expected del */ U"ê"
},
{
@ -263,9 +244,23 @@ const TestData values[] = {
/* cached context post transform: */ &items_2[0],
/* action del, output: */ 1, U"a\U0001F60E",
// ---- results ----
/* action del, output: */ 1, U"a\U0001F60E",
/* app_context: */ u"a\U0001F607bca\U0001F60E"
/* expected action del, output: */ 1, U"a\U0001F60E",
/* expected app_context: */ u"a\U0001F607bca\U0001F60E",
/* expected del */ U"ê"
},
// regression #11067
{
"ANonBmpCharInContext11067",
/* app context pre transform: */ u"𐒻",
/* cached context post transform: */ u"𐒻𐒷",
/* cached context post transform: */ &items_11067[0],
/* action del, output: */ 1, U"𐒻𐒷",
// ---- results ----
/* expected action del, output: */ 1, U"𐒻𐒷",
/* expected app_context: */ u"𐒻𐒷",
/* expected del: */ U"\x104BB"
}
};
INSTANTIATE_TEST_SUITE_P(KeymanCore, ActionsUpdateAppContextNfuApiTest, testing::ValuesIn(values), GenerateTestName);
INSTANTIATE_TEST_SUITE_P(KeymanCore, ActionsUpdateAppContextNfuApiTest, testing::ValuesIn(actionsTestDataNFU), GenerateTestName);

View file

@ -1352,23 +1352,23 @@ TEST(XStringTests, TestU32stringToU16string) {
TEST(XStringTests, TestIsValid) {
// valid
ASSERT_EQ(Uni_IsValid(0x0000), true);
ASSERT_EQ(Uni_IsValid(0x0127), true);
ASSERT_EQ(Uni_IsValid(U'🙀'), true);
ASSERT_TRUE(Uni_IsValid(0x0000));
ASSERT_TRUE(Uni_IsValid(0x0127));
ASSERT_TRUE(Uni_IsValid(U'🙀'));
// invalid
ASSERT_EQ(Uni_IsValid(0xDECAFBAD), false); // out of range
ASSERT_EQ(Uni_IsValid(0x566D4128), false);
ASSERT_EQ(Uni_IsValid(0xFFFF), false); // nonchar
ASSERT_EQ(Uni_IsValid(0xFFFE), false); // nonchar
ASSERT_EQ(Uni_IsValid(0x10FFFF), false); // nonchar
ASSERT_EQ(Uni_IsValid(0x10FFFE), false); // nonchar
ASSERT_EQ(Uni_IsValid(0x01FFFF), false); // nonchar
ASSERT_EQ(Uni_IsValid(0x01FFFE), false); // nonchar
ASSERT_EQ(Uni_IsValid(0x02FFFF), false); // nonchar
ASSERT_EQ(Uni_IsValid(0x02FFFE), false); // nonchar
ASSERT_EQ(Uni_IsValid(0xFDD1), false); // nonchar
ASSERT_EQ(Uni_IsValid(0xFDD0), false); // nonchar
ASSERT_FALSE(Uni_IsValid(0xDECAFBAD)); // out of range
ASSERT_FALSE(Uni_IsValid(0x566D4128));
ASSERT_FALSE(Uni_IsValid(0xFFFF)); // nonchar
ASSERT_FALSE(Uni_IsValid(0xFFFE)); // nonchar
ASSERT_FALSE(Uni_IsValid(0x10FFFF)); // nonchar
ASSERT_FALSE(Uni_IsValid(0x10FFFE)); // nonchar
ASSERT_FALSE(Uni_IsValid(0x01FFFF)); // nonchar
ASSERT_FALSE(Uni_IsValid(0x01FFFE)); // nonchar
ASSERT_FALSE(Uni_IsValid(0x02FFFF)); // nonchar
ASSERT_FALSE(Uni_IsValid(0x02FFFE)); // nonchar
ASSERT_FALSE(Uni_IsValid(0xFDD1)); // nonchar
ASSERT_FALSE(Uni_IsValid(0xFDD0)); // nonchar
// positive range test

View file

@ -40,15 +40,15 @@ tests = [
['debug-api-tests', 'debug_api.tests.cpp'],
['kmx_xstring-tests', 'kmx_xstring.tests.cpp'],
['kmx_context-tests', 'kmx_context.tests.cpp'],
['actions_normalize-tests', 'actions_normalize.tests.cpp'],
['actions_normalize-tests', ['actions_normalize.tests.cpp', 'actions_test_data.cpp']],
# renamed to avoid heuristic that causes "update" in a filename to require
# elevation on Windows! this could also be avoided by adding a manifest
# resource to each executable (e.g. with mt.exe)
# https://learn.microsoft.com/en-us/windows/security/application-security/application-control/user-account-control/architecture#installer-detection-technology
['actions_updatx_app_context_nfu-tests', 'actions_update_app_context_nfu.tests.cpp'],
['actions_updatx_app_context_nfu-tests', ['actions_update_app_context_nfu.tests.cpp', 'actions_test_data.cpp']],
['actions_get_api-tests', 'actions_get_api.tests.cpp'],
['actions_get_api-tests', ['actions_get_api.tests.cpp', 'actions_test_data.cpp']],
['km_core_keyboard_api-tests', 'km_core_keyboard_api.tests.cpp'],
['km_core_process_event-tests', 'km_core_process_event.tests.cpp'],
['key_list-tests', 'kmx_key_list.tests.cpp'],

View file

@ -22,7 +22,6 @@ class StateContextApiTests : public testing::Test {
protected:
km_core_keyboard *test_kb = nullptr;
km_core_state *test_state = nullptr;
km_core_context_item *citems = nullptr;
void Initialize(const char *keyboard, const km_core_cu *context, bool setup_app_context = true) {
km::core::path path = km::core::path(test_dir / ".." / "kmx" / keyboard);
@ -36,18 +35,20 @@ protected:
ASSERT_STATUS_OK(km_core_keyboard_load_from_blob(path.stem().c_str(), blob.data(), blob.size(), &test_kb));
}
ASSERT_STATUS_OK(km_core_state_create(test_kb, test_empty_env_opts, &test_state));
ASSERT_STATUS_OK(context_items_from_utf16(context, &citems));
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), citems));
km_core_context_item *context_items = nullptr;
ASSERT_STATUS_OK(context_items_from_utf16(context, &context_items));
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), context_items));
if (setup_app_context) {
ASSERT_STATUS_OK(km_core_context_set(km_core_state_app_context(test_state), citems));
ASSERT_STATUS_OK(km_core_context_set(km_core_state_app_context(test_state), context_items));
}
if (context_items) {
km_core_context_items_dispose(context_items);
context_items = nullptr;
}
}
void TearDown() override {
if (citems) {
km_core_context_items_dispose(citems);
citems = nullptr;
}
if (test_state) {
km_core_state_dispose(test_state);
test_state = nullptr;
@ -57,57 +58,63 @@ protected:
test_kb = nullptr;
}
}
void assert_identical_context(km_core_cu const *expected_context) {
size_t buf_size;
ASSERT_STATUS_OK(km_core_context_get(km_core_state_context(test_state), &citems));
ASSERT_STATUS_OK(context_items_to_utf16(citems, nullptr, &buf_size));
km_core_cu *actual_context = new km_core_cu[buf_size];
ASSERT_STATUS_OK(context_items_to_utf16(citems, actual_context, &buf_size));
ASSERT_EQ(std::u16string(expected_context), actual_context);
delete[] actual_context;
}
void assert_different_context(km_core_cu const *expected_context) {
size_t buf_size;
ASSERT_STATUS_OK(km_core_context_get(km_core_state_context(test_state), &citems));
ASSERT_STATUS_OK(context_items_to_utf16(citems, nullptr, &buf_size));
km_core_cu *actual_context = new km_core_cu[buf_size];
ASSERT_STATUS_OK(context_items_to_utf16(citems, actual_context, &buf_size));
ASSERT_NE(std::u16string(expected_context), actual_context);
delete[] actual_context;
}
};
void assert_identical_context(const km_core_context *actual_context, km_core_cu const *expected_context) {
km_core_context_item *actual_context_items;
size_t buf_size;
ASSERT_STATUS_OK(km_core_context_get(actual_context, &actual_context_items));
ASSERT_STATUS_OK(context_items_to_utf16(actual_context_items, nullptr, &buf_size));
km_core_cu *actual_context_cu = new km_core_cu[buf_size];
ASSERT_STATUS_OK(context_items_to_utf16(actual_context_items, actual_context_cu, &buf_size));
km_core_context_items_dispose(actual_context_items);
void assert_identical_context_with_markers(const km_core_context *context, const km_core_context_item *citems) {
km_core_context_item *citems_new;
ASSERT_STATUS_OK(km_core_context_get(context, &citems_new));
for (int i = 0; citems[i].type || citems_new[i].type; i++) {
ASSERT_EQ(citems_new[i].type, citems[i].type) << "Unexpected type:";
if (citems[i].type == KM_CORE_CT_CHAR) {
ASSERT_EQ(citems_new[i].character, citems[i].character) << "Unexpected character:";
} else {
ASSERT_EQ(citems_new[i].marker, citems[i].marker) << "Unexpected marker:";
}
}
km_core_context_items_dispose(citems_new);
ASSERT_EQ(actual_context_cu, std::u16string(expected_context));
delete[] actual_context_cu;
}
// citems contains markers, but we will skip over them
void assert_identical_context_without_markers(const km_core_context *context, const km_core_context_item *citems) {
km_core_context_item *citems_new;
ASSERT_STATUS_OK(km_core_context_get(context, &citems_new));
for (int i = 0, i_new = 0; citems[i].type || citems_new[i_new].type; i++) {
if (citems[i].type == KM_CORE_CT_CHAR) {
ASSERT_EQ(citems_new[i_new].type, citems[i].type) << "Unexpected type:";
ASSERT_EQ(citems_new[i_new].character, citems[i].character) << "Unexpected character:";
i_new++;
} else if(citems[i].type == KM_CORE_CT_END) {
ASSERT_EQ(citems_new[i_new].type, citems[i].type) << "Unexpected type:";
void assert_different_context(const km_core_context *actual_context, km_core_cu const *expected_context) {
km_core_context_item *actual_context_items;
size_t buf_size;
ASSERT_STATUS_OK(km_core_context_get(actual_context, &actual_context_items));
ASSERT_STATUS_OK(context_items_to_utf16(actual_context_items, nullptr, &buf_size));
km_core_cu *actual_context_cu = new km_core_cu[buf_size];
ASSERT_STATUS_OK(context_items_to_utf16(actual_context_items, actual_context_cu, &buf_size));
km_core_context_items_dispose(actual_context_items);
ASSERT_NE(actual_context_cu, std::u16string(expected_context));
delete[] actual_context_cu;
}
void assert_identical_context_with_markers(const km_core_context *actual_context, const km_core_context_item *expected_context_items) {
km_core_context_item *actual_context_items;
ASSERT_STATUS_OK(km_core_context_get(actual_context, &actual_context_items));
for (int i = 0; expected_context_items[i].type || actual_context_items[i].type; i++) {
ASSERT_EQ(actual_context_items[i].type, expected_context_items[i].type) << "Unexpected type:";
if (expected_context_items[i].type == KM_CORE_CT_CHAR) {
ASSERT_EQ(actual_context_items[i].character, expected_context_items[i].character) << "Unexpected character:";
} else {
ASSERT_EQ(actual_context_items[i].marker, expected_context_items[i].marker) << "Unexpected marker:";
}
}
km_core_context_items_dispose(citems_new);
km_core_context_items_dispose(actual_context_items);
}
// expected_context_items contains markers, but we will skip over them
void assert_identical_context_without_markers(const km_core_context *actual_context, const km_core_context_item *expected_context_items) {
km_core_context_item *actual_context_items;
ASSERT_STATUS_OK(km_core_context_get(actual_context, &actual_context_items));
for (int i = 0, i_new = 0; expected_context_items[i].type || actual_context_items[i_new].type; i++) {
if (expected_context_items[i].type == KM_CORE_CT_CHAR) {
ASSERT_EQ(actual_context_items[i_new].type, expected_context_items[i].type) << "Unexpected type:";
ASSERT_EQ(actual_context_items[i_new].character, expected_context_items[i].character) << "Unexpected character:";
i_new++;
} else if(expected_context_items[i].type == KM_CORE_CT_END) {
ASSERT_EQ(actual_context_items[i_new].type, expected_context_items[i].type) << "Unexpected type:";
}
}
km_core_context_items_dispose(actual_context_items);
}
// Scenarios from #10100:
@ -118,7 +125,7 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededIdenticalContext) {
km_core_cu const *new_app_context = u"This is a test";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context, false));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UNCHANGED);
ASSERT_NO_FATAL_FAILURE(assert_identical_context(cached_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(km_core_state_context(test_state), cached_context));
}
// 1a. cached context has markers and is identical to app context
@ -127,17 +134,17 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededIdenticalContextAndMarkers) {
km_core_cu const *new_app_context = u"123";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
km_core_context_item const citems[] = {
km_core_context_item const expected_context_items[] = {
{KM_CORE_CT_MARKER, {0}, {5}}, {KM_CORE_CT_CHAR, {0}, {'1'}}, {KM_CORE_CT_MARKER, {0}, {1}},
{KM_CORE_CT_CHAR, {0}, {'2'}}, {KM_CORE_CT_MARKER, {0}, {2}}, {KM_CORE_CT_CHAR, {0}, {'3'}},
{KM_CORE_CT_MARKER, {0}, {3}}, {KM_CORE_CT_MARKER, {0}, {4}}, KM_CORE_CONTEXT_ITEM_END};
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), citems));
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), expected_context_items));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UNCHANGED);
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_without_markers(km_core_state_app_context(test_state), citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_context_items));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_without_markers(km_core_state_app_context(test_state), expected_context_items));
}
// 2. cached context same length as app context but content is different
@ -146,8 +153,8 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededDifferentContext) {
km_core_cu const *new_app_context = u"This is a test";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UPDATED);
ASSERT_NO_FATAL_FAILURE(assert_different_context(cached_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(new_app_context));
ASSERT_NO_FATAL_FAILURE(assert_different_context(km_core_state_context(test_state), cached_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(km_core_state_context(test_state), new_app_context));
}
// 3. cached context is shorter than app context, but content is same as far as it goes
@ -156,8 +163,8 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededAppContextIsLonger) {
km_core_cu const *new_app_context = u"Longer This is a test";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UPDATED);
ASSERT_NO_FATAL_FAILURE(assert_different_context(cached_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(new_app_context));
ASSERT_NO_FATAL_FAILURE(assert_different_context(km_core_state_context(test_state), cached_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(km_core_state_context(test_state), new_app_context));
}
// 3a. cached context has markers and is shorter than app context,
@ -167,22 +174,22 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededCachedContextShorterAndMarker
km_core_cu const *new_app_context = u"0123";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
km_core_context_item const citems[] = {
km_core_context_item const initial_context_items[] = {
{KM_CORE_CT_MARKER, {0}, {5}}, {KM_CORE_CT_CHAR, {0}, {'1'}}, {KM_CORE_CT_MARKER, {0}, {1}},
{KM_CORE_CT_CHAR, {0}, {'2'}}, {KM_CORE_CT_MARKER, {0}, {2}}, {KM_CORE_CT_CHAR, {0}, {'3'}},
{KM_CORE_CT_MARKER, {0}, {3}}, {KM_CORE_CT_MARKER, {0}, {4}}, KM_CORE_CONTEXT_ITEM_END};
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), citems));
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), initial_context_items));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UPDATED);
km_core_context_item const expected_citems[] = {
km_core_context_item const expected_context_items[] = {
{KM_CORE_CT_CHAR, {0}, {'0'}},
{KM_CORE_CT_MARKER, {0}, {5}}, {KM_CORE_CT_CHAR, {0}, {'1'}}, {KM_CORE_CT_MARKER, {0}, {1}},
{KM_CORE_CT_CHAR, {0}, {'2'}}, {KM_CORE_CT_MARKER, {0}, {2}}, {KM_CORE_CT_CHAR, {0}, {'3'}},
{KM_CORE_CT_MARKER, {0}, {3}}, {KM_CORE_CT_MARKER, {0}, {4}}, KM_CORE_CONTEXT_ITEM_END};
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_without_markers(km_core_state_app_context(test_state), expected_citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(new_app_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_context_items));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_without_markers(km_core_state_app_context(test_state), expected_context_items));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(km_core_state_context(test_state), new_app_context));
}
TEST_F(StateContextApiTests, TestContextSetIfNeededCachedContextShorterAndMarkersNfu) {
@ -190,22 +197,22 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededCachedContextShorterAndMarker
km_core_cu const *new_app_context = u"abcệ";
ASSERT_NO_FATAL_FAILURE(Initialize("/a/dummy/keyboard.mock", cached_context));
km_core_context_item const citems[] = {
km_core_context_item const initial_context_items[] = {
{KM_CORE_CT_MARKER, {0}, {1}}, {KM_CORE_CT_CHAR, {0}, {'b'}}, {KM_CORE_CT_MARKER, {0}, {2}},
{KM_CORE_CT_CHAR, {0}, {'c'}}, {KM_CORE_CT_MARKER, {0}, {3}}, {KM_CORE_CT_MARKER, {0}, {4}},
{KM_CORE_CT_CHAR, {0}, {'e'}}, {KM_CORE_CT_CHAR, {0}, {u'\u0323'}},
{KM_CORE_CT_CHAR, {0}, {u'\u0302'}}, KM_CORE_CONTEXT_ITEM_END};
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), citems));
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), initial_context_items));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UPDATED);
km_core_context_item const expected_citems[] = {
km_core_context_item const expected_context_items[] = {
{KM_CORE_CT_CHAR, {0}, {'a'}},
{KM_CORE_CT_MARKER, {0}, {1}}, {KM_CORE_CT_CHAR, {0}, {'b'}}, {KM_CORE_CT_MARKER, {0}, {2}},
{KM_CORE_CT_CHAR, {0}, {'c'}}, {KM_CORE_CT_MARKER, {0}, {3}}, {KM_CORE_CT_MARKER, {0}, {4}},
{KM_CORE_CT_CHAR, {0}, {'e'}}, {KM_CORE_CT_CHAR, {0}, {u'\u0323'}},
{KM_CORE_CT_CHAR, {0}, {u'\u0302'}}, KM_CORE_CONTEXT_ITEM_END};
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_context_items));
km_core_context_item const expected_app_citems[] = {
{KM_CORE_CT_CHAR, {0}, {'a'}},
{KM_CORE_CT_CHAR, {0}, {'b'}},
@ -221,8 +228,8 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededCachedContextCleared) {
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
km_core_state_context_clear(test_state);
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UPDATED);
ASSERT_NO_FATAL_FAILURE(assert_different_context(cached_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(new_app_context));
ASSERT_NO_FATAL_FAILURE(assert_different_context(km_core_state_context(test_state), cached_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(km_core_state_context(test_state), new_app_context));
}
// 5. cached context is longer than app context, but content is same as far as it goes
@ -231,8 +238,8 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededAppContextIsShorter) {
km_core_cu const *new_app_context = u"is a test";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UPDATED);
ASSERT_NO_FATAL_FAILURE(assert_different_context(cached_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(new_app_context));
ASSERT_NO_FATAL_FAILURE(assert_different_context(km_core_state_context(test_state), cached_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(km_core_state_context(test_state), new_app_context));
}
// 5a. cached context has markers and is longer than app context, but
@ -242,21 +249,21 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededCachedContextLongerAndMarkers
km_core_cu const *new_app_context = u"123";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
km_core_context_item const citems[] = {
km_core_context_item const initial_context_items[] = {
{KM_CORE_CT_CHAR, {0}, {'0'}},
{KM_CORE_CT_MARKER, {0}, {5}}, {KM_CORE_CT_CHAR, {0}, {'1'}}, {KM_CORE_CT_MARKER, {0}, {1}},
{KM_CORE_CT_CHAR, {0}, {'2'}}, {KM_CORE_CT_MARKER, {0}, {2}}, {KM_CORE_CT_CHAR, {0}, {'3'}},
{KM_CORE_CT_MARKER, {0}, {3}}, {KM_CORE_CT_MARKER, {0}, {4}}, KM_CORE_CONTEXT_ITEM_END};
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), citems));
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), initial_context_items));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UPDATED);
km_core_context_item const expected_citems[] = {
km_core_context_item const expected_context_items[] = {
{KM_CORE_CT_MARKER, {0}, {5}}, {KM_CORE_CT_CHAR, {0}, {'1'}}, {KM_CORE_CT_MARKER, {0}, {1}},
{KM_CORE_CT_CHAR, {0}, {'2'}}, {KM_CORE_CT_MARKER, {0}, {2}}, {KM_CORE_CT_CHAR, {0}, {'3'}},
{KM_CORE_CT_MARKER, {0}, {3}}, {KM_CORE_CT_MARKER, {0}, {4}}, KM_CORE_CONTEXT_ITEM_END};
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_without_markers(km_core_state_app_context(test_state), expected_citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_context_items));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_without_markers(km_core_state_app_context(test_state), expected_context_items));
}
TEST_F(StateContextApiTests, TestContextSetIfNeededCachedContextLongerAndMarkersNfu) {
@ -264,22 +271,22 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededCachedContextLongerAndMarkers
km_core_cu const *new_app_context = u"bcệ";
ASSERT_NO_FATAL_FAILURE(Initialize("/a/dummy/keyboard.mock", cached_context));
km_core_context_item const citems[] = {
km_core_context_item const initial_context_items[] = {
{KM_CORE_CT_CHAR, {0}, {'a'}},
{KM_CORE_CT_MARKER, {0}, {1}}, {KM_CORE_CT_CHAR, {0}, {'b'}}, {KM_CORE_CT_MARKER, {0}, {2}},
{KM_CORE_CT_CHAR, {0}, {'c'}}, {KM_CORE_CT_MARKER, {0}, {3}}, {KM_CORE_CT_MARKER, {0}, {4}},
{KM_CORE_CT_CHAR, {0}, {'e'}}, {KM_CORE_CT_CHAR, {0}, {u'\u0323'}},
{KM_CORE_CT_CHAR, {0}, {u'\u0302'}}, KM_CORE_CONTEXT_ITEM_END};
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), citems));
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), initial_context_items));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UPDATED);
km_core_context_item const expected_citems[] = {
km_core_context_item const expected_context_items[] = {
{KM_CORE_CT_MARKER, {0}, {1}}, {KM_CORE_CT_CHAR, {0}, {'b'}}, {KM_CORE_CT_MARKER, {0}, {2}},
{KM_CORE_CT_CHAR, {0}, {'c'}}, {KM_CORE_CT_MARKER, {0}, {3}}, {KM_CORE_CT_MARKER, {0}, {4}},
{KM_CORE_CT_CHAR, {0}, {'e'}}, {KM_CORE_CT_CHAR, {0}, {u'\u0323'}},
{KM_CORE_CT_CHAR, {0}, {u'\u0302'}}, KM_CORE_CONTEXT_ITEM_END};
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_context_items));
km_core_context_item const expected_app_citems[] = {
{KM_CORE_CT_CHAR, {0}, {'b'}},
@ -295,8 +302,8 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededApplicationContextEmpty) {
km_core_cu const *new_app_context = u"";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UPDATED);
ASSERT_NO_FATAL_FAILURE(assert_different_context(cached_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(new_app_context));
ASSERT_NO_FATAL_FAILURE(assert_different_context(km_core_state_context(test_state), cached_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(km_core_state_context(test_state), new_app_context));
}
// 7. surrogate pairs in context
@ -305,7 +312,7 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededSurrogatePairsUnchanged) {
km_core_cu const *new_app_context = u"a\U00010100";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UNCHANGED);
ASSERT_NO_FATAL_FAILURE(assert_identical_context(new_app_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(km_core_state_context(test_state), new_app_context));
}
TEST_F(StateContextApiTests, TestContextSetIfNeededSurrogatePairsAppContextLonger) {
@ -313,7 +320,7 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededSurrogatePairsAppContextLonge
km_core_cu const *new_app_context = u"xa\U00010100";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UPDATED);
ASSERT_NO_FATAL_FAILURE(assert_identical_context(new_app_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(km_core_state_context(test_state), new_app_context));
}
TEST_F(StateContextApiTests, TestContextSetIfNeededSurrogatePairsCachedContextLonger) {
@ -321,7 +328,7 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededSurrogatePairsCachedContextLo
km_core_cu const *new_app_context = u"a\U00010100";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UPDATED);
ASSERT_NO_FATAL_FAILURE(assert_identical_context(new_app_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(km_core_state_context(test_state), new_app_context));
}
TEST_F(StateContextApiTests, TestContextSetIfNeededSurrogatePairsUnchangedAndMarkers) {
@ -329,16 +336,16 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededSurrogatePairsUnchangedAndMar
km_core_cu const *new_app_context = u"a\U00010100";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
km_core_context_item const citems[] = {
km_core_context_item const expected_context_items[] = {
{KM_CORE_CT_CHAR, {0}, {'a'}},
{KM_CORE_CT_MARKER, {0}, {5}},
{KM_CORE_CT_CHAR, {0}, {0x10100}},
KM_CORE_CONTEXT_ITEM_END};
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), citems));
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), expected_context_items));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UNCHANGED);
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_without_markers(km_core_state_app_context(test_state), citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_context_items));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_without_markers(km_core_state_app_context(test_state), expected_context_items));
}
TEST_F(StateContextApiTests, TestContextSetIfNeededSurrogatePairsAppContextLongerAndMarkers) {
@ -346,23 +353,23 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededSurrogatePairsAppContextLonge
km_core_cu const *new_app_context = u"\U00010200a\U00010100";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
km_core_context_item const citems[] = {
km_core_context_item const initial_context_items[] = {
{KM_CORE_CT_CHAR, {0}, {'a'}},
{KM_CORE_CT_MARKER, {0}, {5}},
{KM_CORE_CT_CHAR, {0}, {0x10100}},
KM_CORE_CONTEXT_ITEM_END};
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), citems));
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), initial_context_items));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UPDATED);
km_core_context_item const expected_citems[] = {
km_core_context_item const expected_context_items[] = {
{KM_CORE_CT_CHAR, {0}, {0x10200}},
{KM_CORE_CT_CHAR, {0}, {'a'}},
{KM_CORE_CT_MARKER, {0}, {5}},
{KM_CORE_CT_CHAR, {0}, {0x10100}},
KM_CORE_CONTEXT_ITEM_END};
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_without_markers(km_core_state_app_context(test_state), expected_citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_context_items));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_without_markers(km_core_state_app_context(test_state), expected_context_items));
}
TEST_F(StateContextApiTests, TestContextSetIfNeededSurrogatePairsCachedContextLongerAndMarkers) {
@ -370,23 +377,23 @@ TEST_F(StateContextApiTests, TestContextSetIfNeededSurrogatePairsCachedContextLo
km_core_cu const *new_app_context = u"a\U00010100";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
km_core_context_item const citems[] = {
km_core_context_item const initial_context_items[] = {
{KM_CORE_CT_CHAR, {0}, {0x10200}},
{KM_CORE_CT_CHAR, {0}, {'a'}},
{KM_CORE_CT_MARKER, {0}, {5}},
{KM_CORE_CT_CHAR, {0}, {0x10100}},
KM_CORE_CONTEXT_ITEM_END};
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), citems));
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), initial_context_items));
ASSERT_EQ(km_core_state_context_set_if_needed(test_state, new_app_context), KM_CORE_CONTEXT_STATUS_UPDATED);
km_core_context_item const expected_citems[] = {
km_core_context_item const expected_context_items[] = {
{KM_CORE_CT_CHAR, {0}, {'a'}},
{KM_CORE_CT_MARKER, {0}, {5}},
{KM_CORE_CT_CHAR, {0}, {0x10100}},
KM_CORE_CONTEXT_ITEM_END};
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_without_markers(km_core_state_app_context(test_state), expected_citems));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_with_markers(km_core_state_context(test_state), expected_context_items));
ASSERT_NO_FATAL_FAILURE(assert_identical_context_without_markers(km_core_state_app_context(test_state), expected_context_items));
}
@ -394,8 +401,8 @@ TEST_F(StateContextApiTests, TestContextClear) {
km_core_cu const *cached_context = u"This is a test";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
ASSERT_STATUS_OK(km_core_state_context_clear(test_state));
ASSERT_NO_FATAL_FAILURE(assert_different_context(cached_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(u""));
ASSERT_NO_FATAL_FAILURE(assert_different_context(km_core_state_context(test_state), cached_context));
ASSERT_NO_FATAL_FAILURE(assert_identical_context(km_core_state_context(test_state), u""));
}
//-------------------------------------------------------------------------------------
@ -413,7 +420,7 @@ TEST_F(StateContextApiTests, TestContextDebugVarious) {
km_core_cu const *cached_context = u"123\U0001F923";
ASSERT_NO_FATAL_FAILURE(Initialize("k_0000___null_keyboard.kmx", cached_context));
km_core_context_item const citems[] = {
km_core_context_item const expected_context_items[] = {
{ KM_CORE_CT_MARKER, {0}, { 5 } },
{ KM_CORE_CT_CHAR, {0}, { '1' } },
{ KM_CORE_CT_MARKER, {0}, { 1 } },
@ -426,7 +433,7 @@ TEST_F(StateContextApiTests, TestContextDebugVarious) {
KM_CORE_CONTEXT_ITEM_END
};
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), citems));
ASSERT_STATUS_OK(km_core_context_set(km_core_state_context(test_state), expected_context_items));
auto str = km_core_state_context_debug(test_state, KM_CORE_DEBUG_CONTEXT_CACHED);
// std::cout << str << std::endl;

View file

@ -77,7 +77,6 @@ void debug_items_equal(
}
}
// TODO-WEB-CORE: avoid cout; instead use google test reporting once all tests are google test
void compare_debug_items(
km_core_state const * state,
std::initializer_list<km_core_state_debug_item> const & expected
@ -86,21 +85,21 @@ void compare_debug_items(
auto act = km_core_state_debug_items(state, &n);
for (auto &rhs: expected) {
if ((int)--n < 0) {
std::cout << "expected longer than actual" << std::endl;
if(n == 0) {
print_debug_item("next expected item:", rhs);
FAIL();
ASSERT_NE(n, 0) << "expected longer than actual";
}
n--;
bool result;
ASSERT_NO_FATAL_FAILURE(debug_items_equal(*act++, rhs, result));
ASSERT_TRUE(result);
}
if(n != 0) {
std::cout << "actual longer than expected" << std::endl;
print_debug_item("next actual item:", *act);
FAIL();
ASSERT_EQ(n, 0) << "actual longer than expected";
}
}

View file

@ -104,13 +104,13 @@ TEST(KMXPlusTest, VkeysHandledCorrectly) {
bool found = false;
ASSERT_EQ(vk.lookup(km::tests::get_vk(
"K_F"), 0, found), u"");
ASSERT_EQ(found, true); // K_F found, but empty string (gap)
ASSERT_TRUE(found); // K_F found, but empty string (gap)
ASSERT_EQ(vk.lookup(km::tests::get_vk(
"K_ENTER"), 0, found), u"");
ASSERT_EQ(found, false); // K_ENTER not found, empty string
ASSERT_FALSE(found); // K_ENTER not found, empty string
ASSERT_EQ(vk.lookup(km::tests::get_vk(
"K_A"), 0, found), u"K_A-0");
ASSERT_EQ(found, true); // expect
ASSERT_TRUE(found); // expect
ASSERT_EQ(vk.lookup(km::tests::get_vk(
"K_A"), LCTRLFLAG, found), u"K_A-LCTRLFLAG");
ASSERT_EQ(vk.lookup(km::tests::get_vk(
@ -173,13 +173,13 @@ TEST(KMXPlusTest, UsetHandledCorrectly) {
};
SimpleUSet u0(&r[0], 2);
ASSERT_EQ(u0.contains(0x62), true); // b
ASSERT_EQ(u0.contains(0x41), false); // A
ASSERT_EQ(u0.contains(0x127), true); // ħ
ASSERT_TRUE(u0.contains(0x62)); // b
ASSERT_FALSE(u0.contains(0x41)); // A
ASSERT_TRUE(u0.contains(0x127)); // ħ
SimpleUSet uempty;
ASSERT_EQ(uempty.contains(0x62), false);
ASSERT_EQ(uempty.contains(0x127), false);
ASSERT_FALSE(uempty.contains(0x62));
ASSERT_FALSE(uempty.contains(0x127));
}
/** tests of the COMP_KMXPLUS_STRS::valid_string() */

View file

@ -2,6 +2,9 @@
* Keyman is copyright (C) SIL International. MIT License.
*
* Keyman Core - Helper to load LDML keyboard test definitions
*
* Note that this file is superficially similar to kmx_test_source.cpp, but the
* parsing and use diverges far enough that they cannot be easily merged.
*/
#include <algorithm>

File diff suppressed because it is too large Load diff