#include "../../../src/ldml/ldml_markers.hpp" #include "../../../src/ldml/ldml_transforms.hpp" #include "../../../src/util_regex.hpp" #include "kmx/kmx_plus.h" #include "kmx/kmx_xstring.h" #include "test_color.h" #include #include #include #include "../../../src/util_regex.hpp" // TODO-LDML: normal asserts wern't working, so using some hacks. // #include "ldml_test_utils.hpp" // #include // #include "debuglog.h" #ifndef zassert_string_equal #define zassert_string_equal(actual, expected) \ { \ if (actual != expected) { \ std::wcerr << __FILE__ << ":" << __LINE__ << ": " << console_color::fg(console_color::BRIGHT_RED) << "got: " << actual \ << " " << km::core::kmx::Debug_UnicodeString(actual, 0) << " expected " << expected << " " \ << km::core::kmx::Debug_UnicodeString(expected, 1) << console_color::reset() << std::endl; \ return EXIT_FAILURE; \ } \ } #endif #ifndef zassert_equal #define zassert_equal(actual, expected) \ { \ if (actual != expected) { \ std::wcerr << __FILE__ << ":" << __LINE__ << ": " << console_color::fg(console_color::BRIGHT_RED) << "got: " << actual \ << " expected " << expected << console_color::reset() << std::endl; \ return EXIT_FAILURE; \ } \ } #endif // needed for streaming operators #include "utfcodec.hpp" using namespace km::core::ldml; using namespace km::core::kmx; void prepend_hex_oct(std::u32string &str, char32_t x) { for (auto i = 0; i < 8; i++) { KMX_DWORD remainder = x & 0xF; // get the last nibble const char32_t ch = remainder < 0xA ? U'0' + remainder : U'A' + (remainder - 0xA); str.insert(0, 1, ch); // prepend x >>= 4; } } std::u32string marker_map_to_string(const marker_map &m) { std::u32string s; for (auto i = m.rbegin(); i < m.rend(); i++) { if (i->end) { s.insert(0, U""); } if (i->processed) { s.insert(0, U""); } if (i->ch == MARKER_BEFORE_EOT) { s.insert(0, U""); } else { prepend_hex_oct(s, i->ch); s.insert(0, U"=U+"); } if (i->marker != LDML_MARKER_NO_INDEX) { prepend_hex_quad(s, i->marker); s.insert(0, U" \\m0x"); } } return s; } bool _assert_marker_map_equal(const char *f, int l, const marker_map orig, const marker_map x) { marker_map a; // copy everything but 'end' entries std::copy_if( orig.begin(), orig.end(), std::back_inserter(a), [](const marker_entry &m) { return m.marker != LDML_MARKER_NO_INDEX; }); // now check equality if (a == x) { return true; } // Not equal. Print out why. std::wcerr << f << ":" << l << ": " << console_color::fg(console_color::BRIGHT_RED); std::wcerr << "got: " << marker_map_to_string(a); std::wcerr << " expected: " << marker_map_to_string(x); std::wcerr << console_color::reset() << std::endl; return false; } #define assert_marker_map_equal(actual, expected) \ if (!_assert_marker_map_equal(__FILE__, __LINE__, (actual), (expected))) \ return EXIT_FAILURE; // using km::core::kmx::u16cmp; int test_transforms() { std::cout << "== " << __FUNCTION__ << std::endl; std::cout << __FILE__ << ":" << __LINE__ << " - basic " << std::endl; { // start with one transform_entry te(std::u32string(U"e\\^"), std::u32string(U"E")); // keep it simple // OK now make a group do it transforms tr(false); transform_group st; st.push_back(te); tr.addGroup(st); // see if we can match the same { std::u32string src(U"barQ^"); bool res = tr.apply(src); zassert_equal(res, false); zassert_string_equal(src, std::u32string(U"barQ^")); // no change } { std::u32string src(U"fooe^"); bool res = tr.apply(src); zassert_equal(res, true); zassert_string_equal(src, std::u32string(U"fooE")); } } std::cout << __FILE__ << ":" << __LINE__ << " - more complex " << std::endl; { transforms tr(false); // setup { transform_group st; st.emplace_back(std::u32string(U"za"), std::u32string(U"c")); st.emplace_back(std::u32string(U"a"), std::u32string(U"bb")); tr.addGroup(st); } { transform_group st; st.emplace_back(std::u32string(U"bb"), std::u32string(U"ccc")); tr.addGroup(st); } { transform_group st; st.emplace_back(std::u32string(U"cc"), std::u32string(U"d")); tr.addGroup(st); } { transform_group st; st.emplace_back(std::u32string(U"tcd"), std::u32string(U"e")); tr.addGroup(st); } // now test // see if we can match the same { std::u32string src(U"ta"); bool res = tr.apply(src); // pipe (|) symbol shows where the 'output' is delineated // t|a --> t|bb --> t|ccc --> t|cd --> |e zassert_string_equal(src, std::u32string(U"e")); zassert_equal(res, true); } { std::u32string src(U"qza"); bool res = tr.apply(src); // pipe (|) symbol shows where the 'output' is delineated // q|za -> q|c zassert_string_equal(src, std::u32string(U"qc")); zassert_equal(res, true); } { std::u32string src(U"qa"); bool res = tr.apply(src); zassert_string_equal(src, std::u32string(U"qcd")); zassert_equal(res, true); } { std::u32string src(U"tb"); bool res = tr.apply(src); zassert_string_equal(src, std::u32string(U"tb")); zassert_equal(res, false); } } std::cout << __FILE__ << ":" << __LINE__ << " - hindi example " << std::endl; { transforms tr(false); { transform_group st; st.emplace_back(std::u32string(U"िह"), std::u32string(U"हि")); tr.addGroup(st); } { std::u32string src(U"िह"); bool res = tr.apply(src); zassert_string_equal(src, std::u32string(U"हि")); zassert_equal(res, true); } } return EXIT_SUCCESS; } int test_reorder_standalone() { std::cout << "== " << __FUNCTION__ << std::endl; std::cout << __FILE__ << ":" << __LINE__ << " - element API test " << std::endl; // element API test - not a real element, just here for testing { element es(U'a', 0xF4500000 | LDML_ELEM_FLAGS_PREBASE | LDML_ELEM_FLAGS_TERTIARY_BASE); // tertiary -12, primary 80 std::cout << "es flags" << std::hex << es.get_flags() << std::dec << std::endl; // verify element metadata test_assert_equal(es.is_uset(), false); test_assert_equal(es.get_order(), 0x50); test_assert_equal(es.get_tertiary(), -12); test_assert_equal(es.is_prebase(), true); test_assert_equal(es.is_tertiary_base(), true); // verify element matching test_assert_equal(es.matches(U'a'), true); test_assert_equal(es.matches(U'b'), false); } std::cout << __FILE__ << ":" << __LINE__ << " - nod-Lana " << std::endl; { const std::u32string roasts[] = { // from the spec U"\u1A21\u1A60\u1A45\u1A6B\u1A76", // 'ideal' U"\u1A21\u1A6B\u1A76\u1A60\u1A45", // vowel first U"\u1A21\u1A6B\u1A60\u1A76\u1A45", // vowel first, NFC U"\u1A21\u1A6B\u1A60\u1A45\u1A76", // tone after lower }; /** we expect this is what comes out the other side */ const std::u32string expect = roasts[0]; // now setup the rules const COMP_KMXPLUS_USET_RANGE ranges[] = {// 0 COMP_KMXPLUS_USET_RANGE(0x1A75, 0x1A79)}; const COMP_KMXPLUS_USET_USET usets[] = {{0, 1, 0xFFFFFFFF}}; const COMP_KMXPLUS_USET_USET &toneMarksUset = usets[0]; const SimpleUSet toneMarks(&ranges[toneMarksUset.range], toneMarksUset.count); // validate that the range [1A75, 1A79] matches test_assert_equal(toneMarks.contains(0x1A76), true); test_assert_equal(toneMarks.contains(0x1A60), false); std::cout << __FILE__ << ":" << __LINE__ << " - element API test " << std::endl; // element test { element es(U'a', (80 << LDML_ELEM_FLAGS_ORDER_BITSHIFT) | LDML_ELEM_FLAGS_PREBASE); // tertiary -12, primary 80 std::cout << "es flags" << std::hex << es.get_flags() << std::dec << std::endl; // verify element metadata test_assert_equal(es.is_uset(), false); test_assert_equal(es.get_order(), 0x50); test_assert_equal(es.get_tertiary(), 0); test_assert_equal(es.is_prebase(), true); test_assert_equal(es.is_tertiary_base(), false); // verify element matching test_assert_equal(es.matches(U'a'), true); test_assert_equal(es.matches(U'b'), false); element eu(toneMarks, 0x37F40000); // element metadata std::cout << "eu flags" << std::hex << eu.get_flags() << std::dec << std::endl; test_assert_equal(eu.is_uset(), true); std::cout << "order" << (int)eu.get_order() << std::endl; test_assert_equal(eu.get_order(), -12); test_assert_equal(eu.get_tertiary(), 55); test_assert_equal(eu.is_prebase(), false); test_assert_equal(eu.is_tertiary_base(), false); // element matching test_assert_equal(eu.matches(U'a'), false); test_assert_equal(eu.matches(U'\u1A76'), true); test_assert_equal(eu.matches(U'\u1A75'), true); element_list l; // '[tones]a' l.emplace_back(es); l.emplace_back(eu); std::cout << __FILE__ << ":" << __LINE__ << " - list test " << std::endl; test_assert_equal(l.match_end(U"asdfasdf"), 0); // no match test_assert_equal(l.match_end(U"a"), 0); // partial substring, fastpath because it's short test_assert_equal(l.match_end(U"\u1A76"), 0); // partial substring, fastpath because it's short test_assert_equal(l.match_end(U"a\u1A76"), 2); // Match test_assert_equal(l.match_end(U"a\u1A75"), 2); // Match test_assert_equal(l.match_end(U"SomethingSomethingSomethinga\u1A76"), 2); // Sub-Match // generate sort keys std::cout << __FILE__ << ":" << __LINE__ << " - get_sort_key test " << std::endl; std::u32string str = U"a\u1A78"; // get the baseline sortkey auto keylist = reorder_sort_key::from(str); std::cout << __FILE__ << ":" << __LINE__ << " baseline sortkey" << std::endl; for (auto i = keylist.begin(); i < keylist.end(); i++) { i->dump(); } // make sure it matches test_assert_equal(l.match_end(str), 2); // update the keylist with these elements. l.update_sort_key(0, keylist); std::cout << __FILE__ << ":" << __LINE__ << " updated sortkey" << std::endl; test_assert_equal(keylist.size(), 2); reorder_weight secondary = 0; for (auto i = keylist.begin(); i < keylist.end(); i++) { i->dump(); test_assert_equal(i->secondary, secondary); test_assert_equal(i->quaternary, secondary); secondary++; } std::cout << std::endl; // spot check first sortkey test_assert_equal(keylist.begin()->primary, 80); test_assert_equal(keylist.begin()->tertiary, 0); test_assert_equal(keylist.begin()->ch, 0x61); std::cout << __FILE__ << ":" << __LINE__ << " sorted sortkey" << std::endl; // now sort them std::sort(keylist.begin(), keylist.end()); for (auto i = keylist.begin(); i < keylist.end(); i++) { i->dump(); } std::cout << std::endl; // spot check first sort key test_assert_equal(keylist.begin()->primary, -12); test_assert_equal(keylist.begin()->tertiary, 55); test_assert_equal(keylist.begin()->ch, 0x1A78); } std::cout << __FILE__ << ":" << __LINE__ << " - key test " << std::endl; { // can we sort an array of reorder sort keys? reorder_sort_key keys0[] = { {U'\u1A21', 0, 0, 0, 0}, {U'\u1A6B', 42, 1, 0, 1}, {U'\u1A76', 55, 2, 0, 2}, {U'\u1A60', 10, 3, 0, 3}, {U'\u1A45', 10, 4, 0, 3}}; // add it to a list std::deque keylist; for (size_t i = 0; i < sizeof(keys0) / sizeof(keys0[0]); i++) { keylist.emplace_back(keys0[i]); // print it out keys0[i].dump(); } // now sort it std::cout << __FILE__ << ":" << __LINE__ << " sort" << std::endl; std::sort(keylist.begin(), keylist.end()); std::u32string sorted; for (auto i = keylist.begin(); i < keylist.end(); i++) { i->dump(); sorted.append(1, i->ch); } // did the roast come out? zassert_string_equal(sorted, expect); } std::cout << "now prepare the reorder elements" << std::endl; transforms tr(false); { reorder_group rg; // element_list e0; e0.emplace_back(U'\u1A60', 127 << LDML_ELEM_FLAGS_ORDER_BITSHIFT); rg.list.emplace_back(e0); // element_list e1; e1.emplace_back(U'\u1A6B', 42 << LDML_ELEM_FLAGS_ORDER_BITSHIFT); rg.list.emplace_back(e1); // element_list e2; e2.emplace_back(toneMarks, 55 << LDML_ELEM_FLAGS_ORDER_BITSHIFT); rg.list.emplace_back(e2); // element_list e3; e3.emplace_back(U'\u1A60', 10 << LDML_ELEM_FLAGS_ORDER_BITSHIFT); e3.emplace_back(U'\u1A45', 10 << LDML_ELEM_FLAGS_ORDER_BITSHIFT); element_list e3before; e3before.emplace_back(U'\u1A6B', 0); rg.list.emplace_back(e3, e3before); // element_list e4; e4.emplace_back(U'\u1A60', 10 << LDML_ELEM_FLAGS_ORDER_BITSHIFT); e4.emplace_back(U'\u1A45', 10 << LDML_ELEM_FLAGS_ORDER_BITSHIFT); element_list e4before; e4before.emplace_back(U'\u1A6B', 0); e4before.emplace_back(toneMarks, 0); rg.list.emplace_back(e4, e4before); // element_list e5; e5.emplace_back(U'\u1A60', 10 << LDML_ELEM_FLAGS_ORDER_BITSHIFT); e5.emplace_back(toneMarks, 55 << LDML_ELEM_FLAGS_ORDER_BITSHIFT); e5.emplace_back(U'\u1A45', 10 << LDML_ELEM_FLAGS_ORDER_BITSHIFT); element_list e5before; e5before.emplace_back(U'\u1A6B', 0); rg.list.emplace_back(e5, e5before); tr.addGroup(rg); } // now actually test it std::cout << __FILE__ << ":" << __LINE__ << " - back to nod-Lana " << std::endl; // TODO-LDML: move this into test code perhaps for (size_t r = 0; r < sizeof(roasts) / sizeof(roasts[0]); r++) { const auto &roast = roasts[r]; std::cout << __FILE__ << ":" << __LINE__ << " - trying roast #" << r << "=" << roast << std::endl; // try apply with string { std::cout << "- try apply(text, output)" << std::endl; std::u32string text = roast; std::u32string output; size_t len = tr.apply(text, output); if (len == 0) { std::cout << " (did not apply)" << std::endl; } else { std::cout << " applied, matchLen= " << len << std::endl; text.resize(text.size() - len); // shrink text.append(output); std::cout << " = " << text << std::endl; } zassert_string_equal(text, expect); } // try all-at-once { std::cout << "- try apply(text)" << std::endl; std::u32string text = roast; if (!tr.apply(text)) { std::cout << " (did not apply)" << std::endl; } else if (text == roast) { std::cout << " (suboptimal: apply returned true but made no change)" << std::endl; } else { std::cout << " changed to " << text; } zassert_string_equal(text, expect); std::cout << " matched (converting all at once)!" << std::endl; } // simulate typing this one char at a time; { std::cout << "- try key-at-a-time" << std::endl; std::u32string text; for (auto ch = roast.begin(); ch < roast.end(); ch++) { // append the string text.append(1, *ch); std::cout << "-: " << text << std::endl; if (!tr.apply(text)) { std::cout << " (did not apply)" << std::endl; } } // now the moment of truth zassert_string_equal(text, expect); std::cout << " matched! (converting char at a time)" << std::endl; std::cout << std::endl; } } // special test { std::cout << __FILE__ << ":" << __LINE__ << " - special test " << std::endl; const std::u32string expect = U"\u1A21\u1A60\u1A45"; // this string shouldn't mutate at all. { std::u32string text = expect; tr.apply(text); zassert_string_equal(text, expect); } { // try submatch std::u32string text = expect; std::u32string output; size_t len = tr.apply(text, output); zassert_string_equal(output, U""); test_assert_equal(len, 0); } } } return EXIT_SUCCESS; } // this test case is also in XML form under 'k_201_*' int test_reorder_esk() { std::cout << "== " << __FUNCTION__ << std::endl; std::cout << __FILE__ << ":" << __LINE__ << " - k_201_reorder_esk (tertiary reordering) " << std::endl; { // now setup the rules // rules are a little bit simplified, having only the vowel 'a' std::cout << "now prepare the reorder elements" << std::endl; transforms tr(false); { reorder_group rg; // { element_list e; e.emplace_back(U'a', (0 << LDML_ELEM_FLAGS_ORDER_BITSHIFT) | LDML_ELEM_FLAGS_TERTIARY_BASE); rg.list.emplace_back(e); } // { element_list e; e.emplace_back(0x0332, (1 << LDML_ELEM_FLAGS_TERTIARY_BITSHIFT)); rg.list.emplace_back(e); } // { element_list e; e.emplace_back(0x305, (2 << LDML_ELEM_FLAGS_TERTIARY_BITSHIFT)); // (should be a unicodeset, but for simplicity we're dropping the U+302) // e.emplace_back(0x302, (2 << LDML_ELEM_FLAGS_TERTIARY_BITSHIFT)); rg.list.emplace_back(e); } // { element_list e; e.emplace_back(U'x', (1 << LDML_ELEM_FLAGS_ORDER_BITSHIFT)); rg.list.emplace_back(e); } // { element_list e; e.emplace_back(U'y', (2 << LDML_ELEM_FLAGS_ORDER_BITSHIFT)); rg.list.emplace_back(e); } // { element_list e; e.emplace_back(U'z', (3 << LDML_ELEM_FLAGS_ORDER_BITSHIFT)); rg.list.emplace_back(e); } tr.addGroup(rg); } // now actually test it std::cout << __FILE__ << ":" << __LINE__ << " - cases " << std::endl; const std::u32string orig_expect[] = { // 1short U"ax\u0305", // orig U"a\u0305x", // expect // 2longer U"az\u0305x\u0332", // orig U"a\u0332\u0305xz", // expect }; // TODO-LDML: move this into test code perhaps for (size_t r = 0; r < sizeof(orig_expect) / sizeof(orig_expect[0]); r += 2) { const auto &orig = orig_expect[r + 0]; const auto &expect = orig_expect[r + 1]; std::cout << __FILE__ << ":" << __LINE__ << " - trying str #" << r + 1 << "=" << orig << std::endl; // try apply with string { std::cout << "- try apply(text, output)" << std::endl; std::u32string text = orig; std::u32string output; size_t len = tr.apply(text, output); if (len == 0) { std::cout << " (did not apply)" << std::endl; } else { std::cout << " applied, matchLen= " << len << std::endl; text.resize(text.size() - len); // shrink text.append(output); std::cout << " = " << text << std::endl; } zassert_string_equal(text, expect); } // try all-at-once { std::cout << "- try apply(text)" << std::endl; std::u32string text = orig; if (!tr.apply(text)) { std::cout << " (did not apply)" << std::endl; } else if (text == orig) { std::cout << " (suboptimal: apply returned true but made no change)" << std::endl; } else { std::cout << " changed to " << text; } zassert_string_equal(text, expect); std::cout << " matched (converting all at once)!" << std::endl; } // simulate typing this one char at a time; { std::cout << "- try key-at-a-time" << std::endl; std::u32string text; for (auto ch = orig.begin(); ch < orig.end(); ch++) { // append the string text.append(1, *ch); std::cout << "-: " << text << std::endl; if (!tr.apply(text)) { std::cout << " (did not apply)" << std::endl; } } // now the moment of truth zassert_string_equal(text, expect); std::cout << " matched! (converting char at a time)" << std::endl; std::cout << std::endl; } } } return EXIT_SUCCESS; } int test_map() { std::cout << "== " << __FUNCTION__ << std::endl; std::cout << __FILE__ << ":" << __LINE__ << " transform_entry::findIndex" << std::endl; { std::deque list; test_assert_equal(km::core::util::km_regex::findIndex(U"Does Not Exist", list), -1); list.emplace_back(U"0th"); list.emplace_back(U"First"); list.emplace_back(U"Second"); test_assert_equal(km::core::util::km_regex::findIndex(U"First", list), 1); test_assert_equal(km::core::util::km_regex::findIndex(U"0th", list), 0); test_assert_equal(km::core::util::km_regex::findIndex(U"Second", list), 2); test_assert_equal(km::core::util::km_regex::findIndex(U"Nowhere", list), -1); } return EXIT_SUCCESS; } int test_strutils() { std::cout << "== " << __FUNCTION__ << std::endl; std::cout << __FILE__ << ":" << __LINE__ << " * remove_markers" << std::endl; { std::cout << __FILE__ << ":" << __LINE__ << " - basic test0" << std::endl; const std::u32string src = U"abc"; const std::u32string dst = remove_markers(src); zassert_string_equal(dst, src); // unchanged } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - basic test" << std::endl; const std::u32string src = U"abc"; const std::u32string dst = remove_markers(src, map); zassert_string_equal(dst, src); // unchanged test_assert_equal(count_markers(map), 0); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - marker test" << std::endl; const std::u32string src = U"6\U0000ffff\U00000008\U00000001e"; const std::u32string dst = remove_markers(src, map); const std::u32string expect = U"6e"; zassert_string_equal(dst, expect); marker_map expm = {{U'e', 0x1L}}; assert_marker_map_equal(map, expm); // marker 1 @ e test_assert_equal(count_markers(map), 1); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - bad0" << std::endl; const std::u32string src = U"6\U0000ffff\U00000008"; // missing trailing marker # const std::u32string dst = remove_markers(src, map); const std::u32string expect = src; zassert_string_equal(dst, expect); test_assert_equal(count_markers(map), 0); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - bad1" << std::endl; const std::u32string src = U"6\U0000ffffq"; // missing sentinel subtype const std::u32string dst = remove_markers(src, map); const std::u32string expect = src; // 'q' removed zassert_string_equal(dst, expect); test_assert_equal(count_markers(map), 0); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - bad1b" << std::endl; const std::u32string src = U"6\U0000ffff"; // missing code const std::u32string dst = remove_markers(src, map); const std::u32string expect = src; zassert_string_equal(dst, expect); test_assert_equal(count_markers(map), 0); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - bad1c" << std::endl; const std::u32string src = U"6\U0000ffffzz"; // missing code const std::u32string dst = remove_markers(src, map); const std::u32string expect = src; zassert_string_equal(dst, expect); test_assert_equal(count_markers(map), 0); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - marker end test" << std::endl; const std::u32string src = U"6\U0000ffff\U00000008\U00000001"; const std::u32string dst = remove_markers(src, map); const std::u32string expect = U"6"; zassert_string_equal(dst, expect); marker_map expm({{MARKER_BEFORE_EOT, 0x1L}}); assert_marker_map_equal(map, expm); test_assert_equal(count_markers(map), 1); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - complex test" << std::endl; const std::u32string src = U"6\U0000ffff\U00000008\U00000001e\U0000ffff\U00000008\U00000002\U00000320\U0000ffff\U00000008\U00000003\U00000300" U"\U0000ffff\U00000008\U00000004"; const std::u32string dst = remove_markers(src, map); const std::u32string expect = U"6e\U00000320\U00000300"; zassert_string_equal(dst, expect); marker_map expm({{U'e', 0x1L}, {0x0320, 0x2L}, {0x0300, 0x3L}, {MARKER_BEFORE_EOT, 0x4L}}); assert_marker_map_equal(map, expm); test_assert_equal(count_markers(map), 4); } { std::cout << __FILE__ << ":" << __LINE__ << " - prepend hex quad" << std::endl; { std::u32string dst; prepend_hex_quad(dst, 0x0001); zassert_string_equal(dst, U"0001"); } { std::u32string dst; prepend_hex_quad(dst, 0xCAFE); zassert_string_equal(dst, U"CAFE"); } { std::u32string dst; prepend_hex_quad(dst, 0xFFFF); zassert_string_equal(dst, U"FFFF"); } } { std::cout << __FILE__ << ":" << __LINE__ << " - parse hex quad" << std::endl; test_assert_equal(parse_hex_quad(U"0001"), 0x0001); test_assert_equal(parse_hex_quad(U"CAFE"), 0xCAFE); test_assert_equal(parse_hex_quad(U"D00d"), 0xD00D); test_assert_equal(parse_hex_quad(U"FFFF"), 0xFFFF); test_assert_equal(parse_hex_quad(U"zzzz"), 0); // err } return EXIT_SUCCESS; } int test_normalize() { std::cout << "== " << __FUNCTION__ << std::endl; std::cout << __FILE__ << ":" << __LINE__ << " * normalize_nfd_markers" << std::endl; { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - noop test" << std::endl; const std::u32string src = U"6e\U00000320\U00000300"; // already NFD const std::u32string expect = src; std::u32string dst = src; test_assert(normalize_nfd_markers_segment(dst, map)); zassert_string_equal(dst, expect); test_assert_equal(count_markers(map), 0); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - medium test" << std::endl; const std::u32string src = U"6e\U00000300\U00000320"; // swapped const std::u32string expect = U"6e\U00000320\U00000300"; // correct NFD std::u32string dst = src; test_assert(normalize_nfd_markers_segment(dst, map)); zassert_string_equal(dst, expect); test_assert_equal(count_markers(map), 0); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - noop test w markers" << std::endl; const std::u32string src = U"6\U0000ffff\U00000008\U00000001e\U0000ffff\U00000008\U00000002\U00000320\U0000ffff\U00000008\U00000003\U00000300" U"\U0000ffff\U00000008\U00000004"; const std::u32string expect = src; std::u32string dst = src; test_assert(normalize_nfd_markers_segment(dst, map)); zassert_string_equal(dst, expect); marker_map expm({{U'e', 0x1L}, {0x320, 0x2L}, {0x300, 0x3L}, {MARKER_BEFORE_EOT, 0x4L}}); assert_marker_map_equal(map, expm); test_assert_equal(count_markers(map), 4); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - complex test" << std::endl; const std::u32string src = // already in order: 320+300 U"6\U0000ffff\U00000008\U00000001e\U0000ffff\U00000008\U00000002\U00000320\U0000ffff\U00000008\U00000003\U00000300" U"\U0000ffff\U00000008\U00000004"; const std::u32string expect = src; std::u32string dst = src; test_assert(normalize_nfd_markers_segment(dst, map)); zassert_string_equal(dst, expect); marker_map expm({{U'e', 0x1L}, {0x320, 0x2L}, {0x300, 0x3L}, {MARKER_BEFORE_EOT, 0x4L}}); assert_marker_map_equal(map, expm); test_assert_equal(count_markers(map), 4); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - complex test2" << std::endl; const std::u32string src = // out of order, 300-320 U"6\U0000ffff\U00000008\U00000001e\U0000ffff\U00000008\U00000002\U00000300\U0000ffff\U00000008\U00000003\U00000320" U"\U0000ffff\U00000008\U00000004"; const std::u32string expect = U"6\U0000ffff\U00000008\U00000001e\U0000ffff\U00000008\U00000003\U00000320\U0000ffff\U00000008\U00000002\U00000300" U"\U0000ffff\U00000008\U00000004"; std::u32string dst = src; test_assert(normalize_nfd_markers_segment(dst, map)); if (dst != expect) { std::cout << "dst: " << Debug_UnicodeString(dst) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect) << std::endl; } zassert_string_equal(dst, expect); marker_map expm({{U'e', 0x1L}, {0x300, 0x2L}, {0x320, 0x3L}, {MARKER_BEFORE_EOT, 0x4L}}); assert_marker_map_equal(map, expm); test_assert_equal(count_markers(map), 4); } { // u"4è\U0000ffff\u0008\U00000001̠" marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - complex test 4a" << std::endl; const std::u32string src = U"4e\u0300\uFFFF\u0008\u0001\u0320"; const std::u32string expect = U"4e\uFFFF\u0008\u0001\u0320\u0300"; std::u32string dst = src; test_assert(normalize_nfd_markers_segment(dst, map)); if (dst != expect) { std::cout << "dst: " << Debug_UnicodeString(dst) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect) << std::endl; } zassert_string_equal(dst, expect); marker_map expm({{0x320, 0x1L}}); assert_marker_map_equal(map, expm); test_assert_equal(count_markers(map), 1); } { // from tests marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - complex test 9c" << std::endl; const std::u32string src = U"9ce\u0300\uFFFF\u0008\u0002\u0320\uFFFF\u0008\u0001"; const std::u32string expect = U"9ce\uFFFF\u0008\u0002\u0320\u0300\uFFFF\u0008\u0001"; std::u32string dst = src; test_assert(normalize_nfd_markers_segment(dst, map)); if (dst != expect) { std::cout << "dst: " << Debug_UnicodeString(dst) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect) << std::endl; } zassert_string_equal(dst, expect); marker_map expm({{0x320, 0x2L}, {MARKER_BEFORE_EOT, 0x1L}}); assert_marker_map_equal(map, expm); test_assert_equal(count_markers(map), 2); } { // from tests - regex edition marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - complex test 9c+regex" << std::endl; const std::u32string src = U"9ce\u0300\\uffff\\u0008\\u0002\u0320\\uffff\\u0008\\u0001"; const std::u32string expect = U"9ce\\uffff\\u0008\\u0002\u0320\u0300\\uffff\\u0008\\u0001"; std::u32string dst = src; test_assert(normalize_nfd_markers_segment(dst, map, regex_sentinel)); if (dst != expect) { std::cout << "dst: " << Debug_UnicodeString(dst) << std::endl; std::cout << " " << dst << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect) << std::endl; std::cout << " " << expect << std::endl; } zassert_string_equal(dst, expect); marker_map expm({{0x320, 0x2L}, {MARKER_BEFORE_EOT, 0x1L}}); assert_marker_map_equal(map, expm); test_assert_equal(count_markers(map), 2); } { // from tests - regex edition marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - complex test \\m{.}" << std::endl; const std::u32string src = U"9ce\u0300\\uffff\\u0008[\\u0001-\\ud7fe]\u0320\\uffff\\u0008\\u0001"; const std::u32string expect = U"9ce\\uffff\\u0008[\\u0001-\\ud7fe]\u0320\u0300\\uffff\\u0008\\u0001"; std::u32string dst = src; test_assert(normalize_nfd_markers_segment(dst, map, regex_sentinel)); if (dst != expect) { std::cout << "dst: " << Debug_UnicodeString(dst) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect) << std::endl; } zassert_string_equal(dst, expect); marker_map expm({{0x320, LDML_MARKER_ANY_INDEX}, {MARKER_BEFORE_EOT, 0x1L}}); assert_marker_map_equal(map, expm); test_assert_equal(count_markers(map), 2); } { // from tests marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - complex test 10 stack o' 2x2" << std::endl; const std::u32string src = U"9ce\u0300\uFFFF\u0008\u0002\uFFFF\u0008\u0002\u0320"; const std::u32string expect = U"9ce\uFFFF\u0008\u0002\uFFFF\u0008\u0002\u0320\u0300"; std::u32string dst = src; test_assert(normalize_nfd_markers_segment(dst, map)); if (dst != expect) { std::cout << "dst: " << Debug_UnicodeString(dst) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect) << std::endl; } zassert_string_equal(dst, expect); marker_map expm({{0x320, 0x2L}, {0x320, 0x2L}}); assert_marker_map_equal(map, expm); test_assert_equal(count_markers(map), 2); } { // from tests marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - complex test 10 stack o' 2x1x2" << std::endl; const std::u32string src = U"9ce\u0300\uFFFF\u0008\u0002\uFFFF\u0008\u0001\uFFFF\u0008\u0003\u0320"; const std::u32string expect = U"9ce\uFFFF\u0008\u0002\uFFFF\u0008\u0001\uFFFF\u0008\u0003\u0320\u0300"; std::u32string dst = src; test_assert(normalize_nfd_markers_segment(dst, map)); if (dst != expect) { std::cout << "dst: " << Debug_UnicodeString(dst) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect) << std::endl; } zassert_string_equal(dst, expect); marker_map expm({{0x320, 0x2L}, {0x320, 0x1L}, {0x320, 0x3L}}); assert_marker_map_equal(map, expm); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - dup-char test" << std::endl; const std::u32string src = U"a\uFFFF\u0008\u0001\u0300e\uFFFF\u0008\u0002\u0300"; const std::u32string dst = remove_markers(src, map); const std::u32string expect = U"a\u0300e\u0300"; // U+0300 twice! This should be removed in 2 segments zassert_string_equal(dst, expect); marker_map expm({{0x300, 0x1L}, {0x300, 0x2L}}); assert_marker_map_equal(map, expm); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - support 2-segment markers " << std::endl; // e\m{1}`\m{2}_E\m{3}`\m{4}_ const std::u32string src = U"e\uFFFF\u0008\u0001\u0300\uFFFF\u0008\u0002\u0320E\uFFFF\u0008\u0003\u0300\uFFFF\u0008\u0004\u0320"; // e\m{2}_\m{1}`E\m{4}_\m{3}` const std::u32string expect_rem = U"e\u0300\u0320E\u0300\u0320"; const std::u32string expect_nfd = U"e\uFFFF\u0008\u0002\u0320\uFFFF\u0008\u0001\u0300E\uFFFF\u0008\u0004\u0320\uFFFF\u0008\u0003\u0300"; auto dst_rem = remove_markers(src, &map); // note: this is bigger than a single segment. so it is a degenerate test case. marker_map expm({{0x300, 0x1L}, {0x320, 0x2L}, {0x300, 0x3L}, {0x320, 0x4L}}); assert_marker_map_equal(map, expm); zassert_string_equal(dst_rem, expect_rem); std::u32string dst_nfd = src; test_assert(normalize_nfd_markers(dst_nfd)); if (dst_nfd != expect_nfd) { std::cout << "dst: " << Debug_UnicodeString(dst_nfd) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect_nfd) << std::endl; } zassert_string_equal(dst_nfd, expect_nfd); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - marker-before-NFC " << std::endl; // KA \m O -> KA \m E AA const std::u32string src = U"\u0995\uFFFF\u0008\u0001\u09CB"; const std::u32string expect_rem = U"\u0995\u09CB"; const std::u32string expect_nfd = U"\u0995\uFFFF\u0008\u0001\u09C7\u09BE"; auto dst_rem = remove_markers(src, &map); marker_map expm({{0x09C7, 0x1L}}); zassert_string_equal(dst_rem, expect_rem); std::u32string dst_nfd = src; test_assert(normalize_nfd_markers(dst_nfd)); if (dst_nfd != expect_nfd) { std::cout << "dst: " << Debug_UnicodeString(dst_nfd) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect_nfd) << std::endl; } zassert_string_equal(dst_nfd, expect_nfd); assert_marker_map_equal(map, expm); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - marker-before-NFC " << std::endl; const std::u32string src = U"\u0995\u09BE\uFFFF\u0008\u0001\u09C7"; const std::u32string expect_rem = U"\u0995\u09BE\u09C7"; const std::u32string expect_nfd = src; // does not get reordered auto dst_rem = remove_markers(src, &map); marker_map expm({{0x09C7, 0x1L}}); zassert_string_equal(dst_rem, expect_rem); std::u32string dst_nfd = src; test_assert(normalize_nfd_markers(dst_nfd)); if (dst_nfd != expect_nfd) { std::cout << "dst: " << Debug_UnicodeString(dst_nfd) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect_nfd) << std::endl; } zassert_string_equal(dst_nfd, expect_nfd); assert_marker_map_equal(map, expm); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - marker-before-NFC " << std::endl; const std::u32string src = U"\u0995\u09BE\uFFFF\u0008\u0001\u09C7"; const std::u32string expect_rem = U"\u0995\u09BE\u09C7"; const std::u32string expect_nfd = src; // does not get reordered auto dst_rem = remove_markers(src, &map); marker_map expm({{0x09C7, 0x1L}}); zassert_string_equal(dst_rem, expect_rem); std::u32string dst_nfd = src; test_assert(normalize_nfd_markers(dst_nfd)); if (dst_nfd != expect_nfd) { std::cout << "dst: " << Debug_UnicodeString(dst_nfd) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect_nfd) << std::endl; } zassert_string_equal(dst_nfd, expect_nfd); assert_marker_map_equal(map, expm); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - marker-before-greek " << std::endl; const std::u32string src = U"\u03B5\uFFFF\u0008\u0001\u0344"; const std::u32string expect_rem = U"\u03B5\u0344"; const std::u32string expect_nfd = U"\u03B5\uFFFF\u0008\u0001\u0308\u0301"; auto dst_rem = remove_markers(src, &map); marker_map expm({{0x0308, 0x1L}}); zassert_string_equal(dst_rem, expect_rem); std::u32string dst_nfd = src; test_assert(normalize_nfd_markers(dst_nfd)); if (dst_nfd != expect_nfd) { std::cout << "dst: " << Debug_UnicodeString(dst_nfd) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect_nfd) << std::endl; } zassert_string_equal(dst_nfd, expect_nfd); assert_marker_map_equal(map, expm); } // doubled markers tests { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - doubled marker1 " << std::endl; const std::u32string src = U"e\uffff\u0008\u0001\u0300\u0320\u0300"; const std::u32string expect_rem = U"e\u0300\u0320\u0300"; const std::u32string expect_nfd = U"e\u0320\uffff\u0008\u0001\u0300\u0300"; auto dst_rem = remove_markers(src, &map); marker_map expm({{0x0300, 0x1L}}); zassert_string_equal(dst_rem, expect_rem); std::u32string dst_nfd = src; test_assert(normalize_nfd_markers(dst_nfd)); if (dst_nfd != expect_nfd) { std::cout << "dst: " << Debug_UnicodeString(dst_nfd) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect_nfd) << std::endl; } zassert_string_equal(dst_nfd, expect_nfd); assert_marker_map_equal(map, expm); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - doubled unchanged marker " << std::endl; const std::u32string src = U"e\u0320\uffff\u0008\u0001\u0300\u0300"; const std::u32string expect_rem = U"e\u0320\u0300\u0300"; const std::u32string expect_nfd = src; auto dst_rem = remove_markers(src, &map); marker_map expm({{0x300, 0x1L}}); zassert_string_equal(dst_rem, expect_rem); std::u32string dst_nfd = src; test_assert(normalize_nfd_markers(dst_nfd)); if (dst_nfd != expect_nfd) { std::cout << "dst: " << Debug_UnicodeString(dst_nfd) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect_nfd) << std::endl; } zassert_string_equal(dst_nfd, expect_nfd); assert_marker_map_equal(map, expm); } { marker_map map; std::cout << __FILE__ << ":" << __LINE__ << " - marker-before-double-greek " << std::endl; const std::u32string src = U"\u03B5\uFFFF\u0008\u0001\u0344\uFFFF\u0008\u0002\u0344\uFFFF\u0008\u0003"; const std::u32string expect_rem = U"\u03B5\u0344\u0344"; const std::u32string expect_nfd = U"\u03B5\uFFFF\u0008\u0001\u0308\u0301\uFFFF\u0008\u0002\u0308\u0301\uFFFF\u0008\u0003"; auto dst_rem = remove_markers(src, &map); marker_map expm({{0x0308, 0x1L},{0x0308, 0x2L},{MARKER_BEFORE_EOT, 0x3L}}); zassert_string_equal(dst_rem, expect_rem); std::u32string dst_nfd = src; test_assert(normalize_nfd_markers(dst_nfd)); if (dst_nfd != expect_nfd) { std::cout << "dst: " << Debug_UnicodeString(dst_nfd) << std::endl; std::cout << "exp: " << Debug_UnicodeString(expect_nfd) << std::endl; } zassert_string_equal(dst_nfd, expect_nfd); assert_marker_map_equal(map, expm); } // macro for moving tests from test-pattern-parser.ts #define TEST_NFD_PLAIN(x, y) \ { \ marker_map map; \ std::cout << __FILE__ << ":" << __LINE__ << ": nfd test " << std::endl; \ const std::u32string src = x; \ const std::u32string expect_nfd = y; \ std::u32string dst_nfd = src; \ test_assert(normalize_nfd_markers(dst_nfd)); \ if (dst_nfd != expect_nfd) { \ std::cout << "dst: " << Debug_UnicodeString(dst_nfd) << std::endl; \ std::cout << "exp: " << Debug_UnicodeString(expect_nfd) << std::endl; \ } \ zassert_string_equal(dst_nfd, expect_nfd); \ } // double marker - in front of second, no change TEST_NFD_PLAIN(U"e\u0320\u0300\uffff\u0008\u0001\u0300", U"e\u0320\u0300\uffff\u0008\u0001\u0300") // double marker - in front of second, with segment reordering TEST_NFD_PLAIN(U"e\u0300\u0320\uffff\u0008\u0001\u0300", U"e\u0320\u0300\uffff\u0008\u0001\u0300") // double marker - alternate pattern with reordering needed TEST_NFD_PLAIN(U"e\u0300\uffff\u0008\u0001\u0300\u0320", U"e\u0320\u0300\uffff\u0008\u0001\u0300") // triple diacritic + marker - reordering needed TEST_NFD_PLAIN(U"e\u0300\uffff\u0008\u0001\u0300\u0320\u0300", U"e\u0320\u0300\uffff\u0008\u0001\u0300\u0300") return EXIT_SUCCESS; } /** test for the util_regex.hpp functions */ int test_util_regex() { std::cout << "== " << __FUNCTION__ << std::endl; { std::cout << __FILE__ << ":" << __LINE__ << " * util_regex.hpp null tests" << std::endl; km::core::util::km_regex r; test_assert(!r.valid()); // not valid because of an empty string } { std::cout << __FILE__ << ":" << __LINE__ << " * util_regex.hpp simple tests" << std::endl; km::core::util::km_regex r(U"ion"); test_assert(r.valid()); const std::u32string to(U"ivity"); const std::deque fromList; const std::deque toList; std::u32string output; auto apply0 = r.apply(U"not present", output, to, fromList, toList); test_assert_equal(apply0, 0); // not found const std::u32string input(U"action"); auto apply1 = r.apply(input, output, to, fromList, toList); test_assert_equal(apply1, 3); // matched last 3 codepoints std::u32string expect(U"ivity"); zassert_string_equal(output, expect) } { std::cout << __FILE__ << ":" << __LINE__ << " * util_regex.hpp wide tests" << std::endl; km::core::util::km_regex r(U"e𐒻"); test_assert(r.valid()); const std::u32string to(U"𐓏"); const std::deque fromList; const std::deque toList; std::u32string output; const std::u32string input(U":e𐒻"); auto apply1 = r.apply(input, output, to, fromList, toList); test_assert_equal(apply1, 2); // matched last 2 codepoints std::u32string expect(U"𐓏"); zassert_string_equal(output, expect) } { std::cout << __FILE__ << ":" << __LINE__ << " * util_regex.hpp simple map tests" << std::endl; km::core::util::km_regex r(U"(A|B|C)"); test_assert(r.valid()); const std::u32string to(U"$[1:alpha2]"); // ignored std::deque fromList; fromList.emplace_back(U"A"); fromList.emplace_back(U"B"); fromList.emplace_back(U"C"); std::deque toList; toList.emplace_back(U"N"); toList.emplace_back(U"O"); toList.emplace_back(U"P"); std::u32string output; auto apply0 = r.apply(U"not present", output, to, fromList, toList); test_assert_equal(apply0, 0); // not found const std::u32string input(U"WHOA"); auto apply1 = r.apply(input, output, to, fromList, toList); test_assert_equal(apply1, 1); // matched last 1 codepoint std::u32string expect(U"N"); zassert_string_equal(output, expect) } { std::cout << __FILE__ << ":" << __LINE__ << " * util_regex.hpp wide map tests" << std::endl; km::core::util::km_regex r(U"(𐒷|𐒻|𐓏𐓏|x)"); test_assert(r.valid()); const std::u32string to(U"$[1:alpha2]"); // ignored std::deque fromList; fromList.emplace_back(U"𐒷"); fromList.emplace_back(U"𐒻"); fromList.emplace_back(U"𐓏𐓏"); fromList.emplace_back(U"x"); std::deque toList; toList.emplace_back(U"x"); toList.emplace_back(U"𐒷"); toList.emplace_back(U"𐒻"); toList.emplace_back(U"𐓏"); std::u32string output; auto apply0 = r.apply(U"not present", output, to, fromList, toList); test_assert_equal(apply0, 0); // not found test_assert_equal(r.apply(U"WHO𐓏𐒷", output, to, fromList, toList), 1); zassert_string_equal(output, U"x"); test_assert_equal(r.apply(U"WHO𐓏x", output, to, fromList, toList), 1); zassert_string_equal(output, U"𐓏"); test_assert_equal(r.apply(U"WHO𐓏𐓏", output, to, fromList, toList), 2); // 2 codepoints zassert_string_equal(output, U"𐒻"); } return EXIT_SUCCESS; } int main(int argc, const char *argv[]) { int rc = EXIT_SUCCESS; bool arg_color = false; int first_arg = 1; if (first_arg < argc) { arg_color = std::string(argv[first_arg]) == "--color"; if (arg_color) { first_arg++; } } console_color::enabled = console_color::isaterminal() || arg_color; if (test_util_regex() != EXIT_SUCCESS) { rc = EXIT_FAILURE; } if (test_transforms() != EXIT_SUCCESS) { rc = EXIT_FAILURE; } if (test_reorder_standalone() != EXIT_SUCCESS) { rc = EXIT_FAILURE; } if (test_reorder_esk() != EXIT_SUCCESS) { rc = EXIT_FAILURE; } if (test_map() != EXIT_SUCCESS) { rc = EXIT_FAILURE; } if (test_strutils() != EXIT_SUCCESS) { rc = EXIT_FAILURE; } if (test_normalize() != EXIT_SUCCESS) { rc = EXIT_FAILURE; } if (rc == EXIT_FAILURE) { std::cout << "== FAILURE" << std::endl; } else { std::cout << "== PASS" << std::endl; } return rc; }