/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "minikin/FontCollection.h" #include "minikin/LayoutPieces.h" #include "FontTestUtils.h" #include "LayoutSplitter.h" #include "UnicodeUtils.h" namespace minikin { namespace { std::pair, Range> parseTestString(const std::string& text) { auto utf16 = utf8ToUtf16(text); Range range; std::vector outText; for (uint16_t c : utf16) { switch (c) { case '(': range.setStart(outText.size()); break; case ')': range.setEnd(outText.size()); break; default: outText.push_back(c); } } return std::make_pair(outText, range); } std::pair parseExpectString(const std::string& text) { auto utf16 = utf8ToUtf16(text); Range context; Range piece; uint32_t textPos = 0; for (uint16_t c : utf16) { switch (c) { case '[': context.setStart(textPos); break; case ']': context.setEnd(textPos); break; case '(': piece.setStart(textPos); break; case ')': piece.setEnd(textPos); break; default: textPos++; } } return std::make_pair(context, piece); } std::string buildDebugString(const U16StringPiece& textBuf, const Range& context, const Range& piece) { std::vector out; out.reserve(textBuf.size() + 4); for (uint32_t i = 0; i < textBuf.size() + 1; ++i) { if (i == context.getStart()) { out.push_back('['); } if (i == piece.getStart()) { out.push_back('('); } if (i == piece.getEnd()) { out.push_back(')'); } if (i == context.getEnd()) { out.push_back(']'); } if (i != textBuf.size()) { out.push_back(textBuf[i]); } } return utf16ToUtf8(out); } TEST(LayoutSplitterTest, LTR_Latin) { struct TestCase { std::string testStr; std::vector expects; } testCases[] = { {"(This is an example text.)", { "[(This)] is an example text.", "This[( )]is an example text.", "This [(is)] an example text.", "This is[( )]an example text.", "This is [(an)] example text.", "This is an[( )]example text.", "This is an [(example)] text.", "This is an example[( )]text.", "This is an example [(text.)]", }}, {"This( is an example )text.", { "This[( )]is an example text.", "This [(is)] an example text.", "This is[( )]an example text.", "This is [(an)] example text.", "This is an[( )]example text.", "This is an [(example)] text.", "This is an example[( )]text.", }}, {"This (is an example) text.", { "This [(is)] an example text.", "This is[( )]an example text.", "This is [(an)] example text.", "This is an[( )]example text.", "This is an [(example)] text.", }}, {"Th(is is an example te)xt.", { "[Th(is)] is an example text.", "This[( )]is an example text.", "This [(is)] an example text.", "This is[( )]an example text.", "This is [(an)] example text.", "This is an[( )]example text.", "This is an [(example)] text.", "This is an example[( )]text.", "This is an example [(te)xt.]", }}, {"This is an ex(amp)le text.", { "This is an [ex(amp)le] text.", }}, {"There are (three spaces.)", { "There are [(three)] spaces.", "There are three[( )] spaces.", "There are three [( )] spaces.", "There are three [( )]spaces.", "There are three [(spaces.)]", }}, }; for (const auto& testCase : testCases) { auto[text, range] = parseTestString(testCase.testStr); uint32_t expectationIndex = 0; for (auto[acContext, acPiece] : LayoutSplitter(text, range, false /* isRtl */)) { ASSERT_NE(expectationIndex, testCase.expects.size()); const std::string expectString = testCase.expects[expectationIndex++]; auto[exContext, exPiece] = parseExpectString(expectString); EXPECT_EQ(acContext, exContext) << expectString << " vs " << buildDebugString(text, acContext, acPiece); EXPECT_EQ(acPiece, exPiece) << expectString << " vs " << buildDebugString(text, acContext, acPiece); } EXPECT_EQ(expectationIndex, testCase.expects.size()) << "Expectations Remains"; } } TEST(LayoutSplitterTest, RTL_Latin) { struct TestCase { std::string testStr; std::vector expects; } testCases[] = { {"(This is an example text.)", { "This is an example [(text.)]", "This is an example[( )]text.", "This is an [(example)] text.", "This is an[( )]example text.", "This is [(an)] example text.", "This is[( )]an example text.", "This [(is)] an example text.", "This[( )]is an example text.", "[(This)] is an example text.", }}, {"This( is an example )text.", { "This is an example[( )]text.", "This is an [(example)] text.", "This is an[( )]example text.", "This is [(an)] example text.", "This is[( )]an example text.", "This [(is)] an example text.", "This[( )]is an example text.", }}, {"This (is an example) text.", { "This is an [(example)] text.", "This is an[( )]example text.", "This is [(an)] example text.", "This is[( )]an example text.", "This [(is)] an example text.", }}, {"Th(is is an example te)xt.", { "This is an example [(te)xt.]", "This is an example[( )]text.", "This is an [(example)] text.", "This is an[( )]example text.", "This is [(an)] example text.", "This is[( )]an example text.", "This [(is)] an example text.", "This[( )]is an example text.", "[Th(is)] is an example text.", }}, {"This is an ex(amp)le text.", { "This is an [ex(amp)le] text.", }}, {"There are (three spaces.)", { "There are three [(spaces.)]", "There are three [( )]spaces.", "There are three [( )] spaces.", "There are three[( )] spaces.", "There are [(three)] spaces.", }}, }; for (const auto& testCase : testCases) { auto[text, range] = parseTestString(testCase.testStr); uint32_t expectationIndex = 0; for (auto[acContext, acPiece] : LayoutSplitter(text, range, true /* isRtl */)) { ASSERT_NE(expectationIndex, testCase.expects.size()); const std::string expectString = testCase.expects[expectationIndex++]; auto[exContext, exPiece] = parseExpectString(expectString); EXPECT_EQ(acContext, exContext) << expectString << " vs " << buildDebugString(text, acContext, acPiece); EXPECT_EQ(acPiece, exPiece) << expectString << " vs " << buildDebugString(text, acContext, acPiece); } EXPECT_EQ(expectationIndex, testCase.expects.size()) << "Expectations Remains"; } } TEST(LayoutSplitterTest, LTR_CJK) { struct TestCase { std::string testStr; std::vector expects; } testCases[] = { {// All Kanji text "(\u6614\u8005\u8358\u5468\u5922\u70BA\u80E1\u8776)", { "[(\u6614)]\u8005\u8358\u5468\u5922\u70BA\u80E1\u8776", "\u6614[(\u8005)]\u8358\u5468\u5922\u70BA\u80E1\u8776", "\u6614\u8005[(\u8358)]\u5468\u5922\u70BA\u80E1\u8776", "\u6614\u8005\u8358[(\u5468)]\u5922\u70BA\u80E1\u8776", "\u6614\u8005\u8358\u5468[(\u5922)]\u70BA\u80E1\u8776", "\u6614\u8005\u8358\u5468\u5922[(\u70BA)]\u80E1\u8776", "\u6614\u8005\u8358\u5468\u5922\u70BA[(\u80E1)]\u8776", "\u6614\u8005\u8358\u5468\u5922\u70BA\u80E1[(\u8776)]", }}, {// Japanese text like as follows // [Kanji][Kanji][Kana][Kanji][Kanji][Kana][Kana][Kana] "(\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002)", { "[(\u672C)]\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", "\u672C[(\u65E5\u306F)]\u6674\u5929\u306A\u308A\u3002", "\u672C\u65E5\u306F[(\u6674)]\u5929\u306A\u308A\u3002", "\u672C\u65E5\u306F\u6674[(\u5929\u306A\u308A\u3002)]", }}, {// Japanese text like as follows // [Kanji][Kanji][Kana][Kanji][Kanji][Kana][Kana][Kana] "\u672C\u65E5(\u306F\u6674\u5929\u306A)\u308A\u3002", { "\u672C[\u65E5(\u306F)]\u6674\u5929\u306A\u308A\u3002", "\u672C\u65E5\u306F[(\u6674)]\u5929\u306A\u308A\u3002", "\u672C\u65E5\u306F\u6674[(\u5929\u306A)\u308A\u3002]", }}, }; for (const auto& testCase : testCases) { auto[text, range] = parseTestString(testCase.testStr); uint32_t expectationIndex = 0; for (auto[acContext, acPiece] : LayoutSplitter(text, range, false /* isRtl */)) { ASSERT_NE(expectationIndex, testCase.expects.size()); const std::string expectString = testCase.expects[expectationIndex++]; auto[exContext, exPiece] = parseExpectString(expectString); EXPECT_EQ(acContext, exContext) << expectString << " vs " << buildDebugString(text, acContext, acPiece); EXPECT_EQ(acPiece, exPiece) << expectString << " vs " << buildDebugString(text, acContext, acPiece); } EXPECT_EQ(expectationIndex, testCase.expects.size()) << "Expectations Remains"; } } TEST(LayoutSplitterTest, RTL_CJK) { struct TestCase { std::string testStr; std::vector expects; } testCases[] = { {// All Kanji text "(\u6614\u8005\u8358\u5468\u5922\u70BA\u80E1\u8776)", { "\u6614\u8005\u8358\u5468\u5922\u70BA\u80E1[(\u8776)]", "\u6614\u8005\u8358\u5468\u5922\u70BA[(\u80E1)]\u8776", "\u6614\u8005\u8358\u5468\u5922[(\u70BA)]\u80E1\u8776", "\u6614\u8005\u8358\u5468[(\u5922)]\u70BA\u80E1\u8776", "\u6614\u8005\u8358[(\u5468)]\u5922\u70BA\u80E1\u8776", "\u6614\u8005[(\u8358)]\u5468\u5922\u70BA\u80E1\u8776", "\u6614[(\u8005)]\u8358\u5468\u5922\u70BA\u80E1\u8776", "[(\u6614)]\u8005\u8358\u5468\u5922\u70BA\u80E1\u8776", }}, {// Japanese text like as follows // [Kanji][Kanji][Kana][Kanji][Kanji][Kana][Kana][Kana] "(\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002)", { "\u672C\u65E5\u306F\u6674[(\u5929\u306A\u308A\u3002)]", "\u672C\u65E5\u306F[(\u6674)]\u5929\u306A\u308A\u3002", "\u672C[(\u65E5\u306F)]\u6674\u5929\u306A\u308A\u3002", "[(\u672C)]\u65E5\u306F\u6674\u5929\u306A\u308A\u3002", }}, {// Japanese text like as follows // [Kanji][Kanji][Kana][Kanji][Kanji][Kana][Kana][Kana] "\u672C\u65E5(\u306F\u6674\u5929\u306A)\u308A\u3002", { "\u672C\u65E5\u306F\u6674[(\u5929\u306A)\u308A\u3002]", "\u672C\u65E5\u306F[(\u6674)]\u5929\u306A\u308A\u3002", "\u672C[\u65E5(\u306F)]\u6674\u5929\u306A\u308A\u3002", }}, }; for (const auto& testCase : testCases) { auto[text, range] = parseTestString(testCase.testStr); uint32_t expectationIndex = 0; for (auto[acContext, acPiece] : LayoutSplitter(text, range, true /* isRtl */)) { ASSERT_NE(expectationIndex, testCase.expects.size()); const std::string expectString = testCase.expects[expectationIndex++]; auto[exContext, exPiece] = parseExpectString(expectString); EXPECT_EQ(acContext, exContext) << expectString << " vs " << buildDebugString(text, acContext, acPiece); EXPECT_EQ(acPiece, exPiece) << expectString << " vs " << buildDebugString(text, acContext, acPiece); } EXPECT_EQ(expectationIndex, testCase.expects.size()) << "Expectations Remains"; } } TEST(LayoutSplitterTest, BidiCtrl) { struct TestCase { std::string testStr; std::vector expects; } testCases[] = { {// Repeated Bidi sequence "(a\u2066\u2069\u202A\u202E\u200E\u200Fb)", { "[(a)]\u2066\u2069\u202A\u202E\u200E\u200Fb", "a[(\u2066)]\u2069\u202A\u202E\u200E\u200Fb", "a\u2066[(\u2069)]\u202A\u202E\u200E\u200Fb", "a\u2066\u2069[(\u202A)]\u202E\u200E\u200Fb", "a\u2066\u2069\u202A[(\u202E)]\u200E\u200Fb", "a\u2066\u2069\u202A\u202E[(\u200E)]\u200Fb", "a\u2066\u2069\u202A\u202E\u200E[(\u200F)]b", "a\u2066\u2069\u202A\u202E\u200E\u200F[(b)]", }}, }; for (const auto& testCase : testCases) { auto [text, range] = parseTestString(testCase.testStr); uint32_t expectationIndex = 0; for (auto [acContext, acPiece] : LayoutSplitter(text, range, false /* isRtl */)) { ASSERT_NE(expectationIndex, testCase.expects.size()); const std::string expectString = testCase.expects[expectationIndex++]; auto [exContext, exPiece] = parseExpectString(expectString); EXPECT_EQ(acContext, exContext) << expectString << " vs " << buildDebugString(text, acContext, acPiece); EXPECT_EQ(acPiece, exPiece) << expectString << " vs " << buildDebugString(text, acContext, acPiece); } EXPECT_EQ(expectationIndex, testCase.expects.size()) << "Expectations Remains"; } } } // namespace } // namespace minikin