// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. #include "precomp.h" #include "WexTestClass.h" #include "..\..\inc\consoletaeftemplates.hpp" #include "CommonState.hpp" #include "../types/inc/CodepointWidthDetector.hpp" using namespace WEX::Logging; static const std::wstring emoji = L"\xD83E\xDD22"; // U+1F922 nauseated face static const std::wstring ambiguous = L"\x414"; // U+0414 cyrillic capital de // codepoint and utf16 encoded string static const std::vector> testData = { { 0x7, L"\a", CodepointWidth::Narrow }, // BEL { 0x20, L" ", CodepointWidth::Narrow }, { 0x39, L"9", CodepointWidth::Narrow }, { 0x414, L"\x414", CodepointWidth::Ambiguous }, // U+0414 cyrillic capital de { 0x1104, L"\x1104", CodepointWidth::Wide }, // U+1104 hangul choseong ssangtikeut { 0x306A, L"\x306A", CodepointWidth::Wide }, // U+306A hiragana na { 0x30CA, L"\x30CA", CodepointWidth::Wide }, // U+30CA katakana na { 0x72D7, L"\x72D7", CodepointWidth::Wide }, // U+72D7 { 0x1F47E, L"\xD83D\xDC7E", CodepointWidth::Wide }, // U+1F47E alien monster { 0x1F51C, L"\xD83D\xDD1C", CodepointWidth::Wide } // U+1F51C SOON }; class CodepointWidthDetectorTests { TEST_CLASS(CodepointWidthDetectorTests); TEST_METHOD(CodepointWidthDetectDefersMapPopulation) { CodepointWidthDetector widthDetector; VERIFY_IS_TRUE(widthDetector._map.empty()); widthDetector.IsWide(UNICODE_SPACE); VERIFY_IS_TRUE(widthDetector._map.empty()); // now force checking widthDetector.GetWidth(emoji); VERIFY_IS_FALSE(widthDetector._map.empty()); } TEST_METHOD(CanLookUpEmoji) { CodepointWidthDetector widthDetector; VERIFY_IS_TRUE(widthDetector.IsWide(emoji)); } TEST_METHOD(TestUnicodeRangeCompare) { CodepointWidthDetector::UnicodeRangeCompare compare; // test comparing 2 search terms CodepointWidthDetector::UnicodeRange a{ 0x10 }; CodepointWidthDetector::UnicodeRange b{ 0x15 }; VERIFY_IS_TRUE(static_cast(compare(a, b))); } TEST_METHOD(CanExtractCodepoint) { CodepointWidthDetector widthDetector; for (const auto& data : testData) { const auto& expected = std::get<0>(data); const auto& wstr = std::get<1>(data); const auto result = widthDetector._extractCodepoint({ wstr.c_str(), wstr.size() }); VERIFY_ARE_EQUAL(result, expected); } } TEST_METHOD(CanGetWidths) { CodepointWidthDetector widthDetector; for (const auto& data : testData) { const auto& expected = std::get<2>(data); const auto& wstr = std::get<1>(data); const auto result = widthDetector.GetWidth({ wstr.c_str(), wstr.size() }); VERIFY_ARE_EQUAL(result, expected); } } static bool FallbackMethod(const std::wstring_view glyph) { if (glyph.size() < 1) { return false; } else { return (glyph.at(0) % 2) == 1; } } TEST_METHOD(AmbiguousCache) { // Set up a detector with fallback. CodepointWidthDetector widthDetector; widthDetector.SetFallbackMethod(std::bind(&FallbackMethod, std::placeholders::_1)); // Ensure fallback cache is empty. VERIFY_ARE_EQUAL(0u, widthDetector._fallbackCache.size()); // Lookup ambiguous width character. widthDetector.IsWide(ambiguous); // Cache should hold it. VERIFY_ARE_EQUAL(1u, widthDetector._fallbackCache.size()); // Cached item should match what we expect const auto it = widthDetector._fallbackCache.begin(); VERIFY_ARE_EQUAL(ambiguous, it->first); VERIFY_ARE_EQUAL(FallbackMethod(ambiguous), it->second); // Cache should empty when font changes. widthDetector.NotifyFontChanged(); VERIFY_ARE_EQUAL(0u, widthDetector._fallbackCache.size()); } };