Home | History | Annotate | Download | only in unittest
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <gtest/gtest.h>
     18 
     19 #include "minikin/FontCollection.h"
     20 #include "minikin/LayoutPieces.h"
     21 
     22 #include "FontTestUtils.h"
     23 #include "LayoutSplitter.h"
     24 #include "UnicodeUtils.h"
     25 
     26 namespace minikin {
     27 namespace {
     28 
     29 std::pair<std::vector<uint16_t>, Range> parseTestString(const std::string& text) {
     30     auto utf16 = utf8ToUtf16(text);
     31     Range range;
     32     std::vector<uint16_t> outText;
     33     for (uint16_t c : utf16) {
     34         switch (c) {
     35             case '(':
     36                 range.setStart(outText.size());
     37                 break;
     38             case ')':
     39                 range.setEnd(outText.size());
     40                 break;
     41             default:
     42                 outText.push_back(c);
     43         }
     44     }
     45     return std::make_pair(outText, range);
     46 }
     47 
     48 std::pair<Range, Range> parseExpectString(const std::string& text) {
     49     auto utf16 = utf8ToUtf16(text);
     50     Range context;
     51     Range piece;
     52     uint32_t textPos = 0;
     53     for (uint16_t c : utf16) {
     54         switch (c) {
     55             case '[':
     56                 context.setStart(textPos);
     57                 break;
     58             case ']':
     59                 context.setEnd(textPos);
     60                 break;
     61             case '(':
     62                 piece.setStart(textPos);
     63                 break;
     64             case ')':
     65                 piece.setEnd(textPos);
     66                 break;
     67             default:
     68                 textPos++;
     69         }
     70     }
     71     return std::make_pair(context, piece);
     72 }
     73 
     74 std::string buildDebugString(const U16StringPiece& textBuf, const Range& context,
     75                              const Range& piece) {
     76     std::vector<uint16_t> out;
     77     out.reserve(textBuf.size() + 4);
     78     for (uint32_t i = 0; i < textBuf.size() + 1; ++i) {
     79         if (i == context.getStart()) {
     80             out.push_back('[');
     81         }
     82         if (i == piece.getStart()) {
     83             out.push_back('(');
     84         }
     85         if (i == piece.getEnd()) {
     86             out.push_back(')');
     87         }
     88         if (i == context.getEnd()) {
     89             out.push_back(']');
     90         }
     91         if (i != textBuf.size()) {
     92             out.push_back(textBuf[i]);
     93         }
     94     }
     95     return utf16ToUtf8(out);
     96 }
     97 
     98 TEST(LayoutSplitterTest, LTR_Latin) {
     99     struct TestCase {
    100         std::string testStr;
    101         std::vector<std::string> expects;
    102     } testCases[] = {
    103             {"(This is an example text.)",
    104              {
    105                      "[(This)] is an example text.", "This[( )]is an example text.",
    106                      "This [(is)] an example text.", "This is[( )]an example text.",
    107                      "This is [(an)] example text.", "This is an[( )]example text.",
    108                      "This is an [(example)] text.", "This is an example[( )]text.",
    109                      "This is an example [(text.)]",
    110              }},
    111             {"This( is an example )text.",
    112              {
    113                      "This[( )]is an example text.", "This [(is)] an example text.",
    114                      "This is[( )]an example text.", "This is [(an)] example text.",
    115                      "This is an[( )]example text.", "This is an [(example)] text.",
    116                      "This is an example[( )]text.",
    117              }},
    118             {"This (is an example) text.",
    119              {
    120                      "This [(is)] an example text.", "This is[( )]an example text.",
    121                      "This is [(an)] example text.", "This is an[( )]example text.",
    122                      "This is an [(example)] text.",
    123              }},
    124             {"Th(is is an example te)xt.",
    125              {
    126                      "[Th(is)] is an example text.", "This[( )]is an example text.",
    127                      "This [(is)] an example text.", "This is[( )]an example text.",
    128                      "This is [(an)] example text.", "This is an[( )]example text.",
    129                      "This is an [(example)] text.", "This is an example[( )]text.",
    130                      "This is an example [(te)xt.]",
    131              }},
    132             {"This is an ex(amp)le text.",
    133              {
    134                      "This is an [ex(amp)le] text.",
    135              }},
    136             {"There are (three   spaces.)",
    137              {
    138                      "There are [(three)]   spaces.", "There are three[( )]  spaces.",
    139                      "There are three [( )] spaces.", "There are three  [( )]spaces.",
    140                      "There are three   [(spaces.)]",
    141              }},
    142     };
    143 
    144     for (const auto& testCase : testCases) {
    145         auto[text, range] = parseTestString(testCase.testStr);
    146         uint32_t expectationIndex = 0;
    147         for (auto[acContext, acPiece] : LayoutSplitter(text, range, false /* isRtl */)) {
    148             ASSERT_NE(expectationIndex, testCase.expects.size());
    149             const std::string expectString = testCase.expects[expectationIndex++];
    150             auto[exContext, exPiece] = parseExpectString(expectString);
    151             EXPECT_EQ(acContext, exContext)
    152                     << expectString << " vs " << buildDebugString(text, acContext, acPiece);
    153             EXPECT_EQ(acPiece, exPiece)
    154                     << expectString << " vs " << buildDebugString(text, acContext, acPiece);
    155         }
    156         EXPECT_EQ(expectationIndex, testCase.expects.size()) << "Expectations Remains";
    157     }
    158 }
    159 
    160 TEST(LayoutSplitterTest, RTL_Latin) {
    161     struct TestCase {
    162         std::string testStr;
    163         std::vector<std::string> expects;
    164     } testCases[] = {
    165             {"(This is an example text.)",
    166              {
    167                      "This is an example [(text.)]", "This is an example[( )]text.",
    168                      "This is an [(example)] text.", "This is an[( )]example text.",
    169                      "This is [(an)] example text.", "This is[( )]an example text.",
    170                      "This [(is)] an example text.", "This[( )]is an example text.",
    171                      "[(This)] is an example text.",
    172              }},
    173             {"This( is an example )text.",
    174              {
    175                      "This is an example[( )]text.", "This is an [(example)] text.",
    176                      "This is an[( )]example text.", "This is [(an)] example text.",
    177                      "This is[( )]an example text.", "This [(is)] an example text.",
    178                      "This[( )]is an example text.",
    179              }},
    180             {"This (is an example) text.",
    181              {
    182                      "This is an [(example)] text.", "This is an[( )]example text.",
    183                      "This is [(an)] example text.", "This is[( )]an example text.",
    184                      "This [(is)] an example text.",
    185              }},
    186             {"Th(is is an example te)xt.",
    187              {
    188                      "This is an example [(te)xt.]", "This is an example[( )]text.",
    189                      "This is an [(example)] text.", "This is an[( )]example text.",
    190                      "This is [(an)] example text.", "This is[( )]an example text.",
    191                      "This [(is)] an example text.", "This[( )]is an example text.",
    192                      "[Th(is)] is an example text.",
    193              }},
    194             {"This is an ex(amp)le text.",
    195              {
    196                      "This is an [ex(amp)le] text.",
    197              }},
    198             {"There are (three   spaces.)",
    199              {
    200                      "There are three   [(spaces.)]", "There are three  [( )]spaces.",
    201                      "There are three [( )] spaces.", "There are three[( )]  spaces.",
    202                      "There are [(three)]   spaces.",
    203              }},
    204     };
    205 
    206     for (const auto& testCase : testCases) {
    207         auto[text, range] = parseTestString(testCase.testStr);
    208         uint32_t expectationIndex = 0;
    209         for (auto[acContext, acPiece] : LayoutSplitter(text, range, true /* isRtl */)) {
    210             ASSERT_NE(expectationIndex, testCase.expects.size());
    211             const std::string expectString = testCase.expects[expectationIndex++];
    212             auto[exContext, exPiece] = parseExpectString(expectString);
    213             EXPECT_EQ(acContext, exContext)
    214                     << expectString << " vs " << buildDebugString(text, acContext, acPiece);
    215             EXPECT_EQ(acPiece, exPiece)
    216                     << expectString << " vs " << buildDebugString(text, acContext, acPiece);
    217         }
    218         EXPECT_EQ(expectationIndex, testCase.expects.size()) << "Expectations Remains";
    219     }
    220 }
    221 
    222 TEST(LayoutSplitterTest, LTR_CJK) {
    223     struct TestCase {
    224         std::string testStr;
    225         std::vector<std::string> expects;
    226     } testCases[] = {
    227             {// All Kanji text
    228              "(\u6614\u8005\u8358\u5468\u5922\u70BA\u80E1\u8776)",
    229              {
    230                      "[(\u6614)]\u8005\u8358\u5468\u5922\u70BA\u80E1\u8776",
    231                      "\u6614[(\u8005)]\u8358\u5468\u5922\u70BA\u80E1\u8776",
    232                      "\u6614\u8005[(\u8358)]\u5468\u5922\u70BA\u80E1\u8776",
    233                      "\u6614\u8005\u8358[(\u5468)]\u5922\u70BA\u80E1\u8776",
    234                      "\u6614\u8005\u8358\u5468[(\u5922)]\u70BA\u80E1\u8776",
    235                      "\u6614\u8005\u8358\u5468\u5922[(\u70BA)]\u80E1\u8776",
    236                      "\u6614\u8005\u8358\u5468\u5922\u70BA[(\u80E1)]\u8776",
    237                      "\u6614\u8005\u8358\u5468\u5922\u70BA\u80E1[(\u8776)]",
    238              }},
    239             {// Japanese text like as follows
    240              // [Kanji][Kanji][Kana][Kanji][Kanji][Kana][Kana][Kana]
    241              "(\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002)",
    242              {
    243                      "[(\u672C)]\u65E5\u306F\u6674\u5929\u306A\u308A\u3002",
    244                      "\u672C[(\u65E5\u306F)]\u6674\u5929\u306A\u308A\u3002",
    245                      "\u672C\u65E5\u306F[(\u6674)]\u5929\u306A\u308A\u3002",
    246                      "\u672C\u65E5\u306F\u6674[(\u5929\u306A\u308A\u3002)]",
    247              }},
    248             {// Japanese text like as follows
    249              // [Kanji][Kanji][Kana][Kanji][Kanji][Kana][Kana][Kana]
    250              "\u672C\u65E5(\u306F\u6674\u5929\u306A)\u308A\u3002",
    251              {
    252                      "\u672C[\u65E5(\u306F)]\u6674\u5929\u306A\u308A\u3002",
    253                      "\u672C\u65E5\u306F[(\u6674)]\u5929\u306A\u308A\u3002",
    254                      "\u672C\u65E5\u306F\u6674[(\u5929\u306A)\u308A\u3002]",
    255              }},
    256     };
    257 
    258     for (const auto& testCase : testCases) {
    259         auto[text, range] = parseTestString(testCase.testStr);
    260         uint32_t expectationIndex = 0;
    261         for (auto[acContext, acPiece] : LayoutSplitter(text, range, false /* isRtl */)) {
    262             ASSERT_NE(expectationIndex, testCase.expects.size());
    263             const std::string expectString = testCase.expects[expectationIndex++];
    264             auto[exContext, exPiece] = parseExpectString(expectString);
    265             EXPECT_EQ(acContext, exContext)
    266                     << expectString << " vs " << buildDebugString(text, acContext, acPiece);
    267             EXPECT_EQ(acPiece, exPiece)
    268                     << expectString << " vs " << buildDebugString(text, acContext, acPiece);
    269         }
    270         EXPECT_EQ(expectationIndex, testCase.expects.size()) << "Expectations Remains";
    271     }
    272 }
    273 
    274 TEST(LayoutSplitterTest, RTL_CJK) {
    275     struct TestCase {
    276         std::string testStr;
    277         std::vector<std::string> expects;
    278     } testCases[] = {
    279             {// All Kanji text
    280              "(\u6614\u8005\u8358\u5468\u5922\u70BA\u80E1\u8776)",
    281              {
    282                      "\u6614\u8005\u8358\u5468\u5922\u70BA\u80E1[(\u8776)]",
    283                      "\u6614\u8005\u8358\u5468\u5922\u70BA[(\u80E1)]\u8776",
    284                      "\u6614\u8005\u8358\u5468\u5922[(\u70BA)]\u80E1\u8776",
    285                      "\u6614\u8005\u8358\u5468[(\u5922)]\u70BA\u80E1\u8776",
    286                      "\u6614\u8005\u8358[(\u5468)]\u5922\u70BA\u80E1\u8776",
    287                      "\u6614\u8005[(\u8358)]\u5468\u5922\u70BA\u80E1\u8776",
    288                      "\u6614[(\u8005)]\u8358\u5468\u5922\u70BA\u80E1\u8776",
    289                      "[(\u6614)]\u8005\u8358\u5468\u5922\u70BA\u80E1\u8776",
    290              }},
    291             {// Japanese text like as follows
    292              // [Kanji][Kanji][Kana][Kanji][Kanji][Kana][Kana][Kana]
    293              "(\u672C\u65E5\u306F\u6674\u5929\u306A\u308A\u3002)",
    294              {
    295                      "\u672C\u65E5\u306F\u6674[(\u5929\u306A\u308A\u3002)]",
    296                      "\u672C\u65E5\u306F[(\u6674)]\u5929\u306A\u308A\u3002",
    297                      "\u672C[(\u65E5\u306F)]\u6674\u5929\u306A\u308A\u3002",
    298                      "[(\u672C)]\u65E5\u306F\u6674\u5929\u306A\u308A\u3002",
    299              }},
    300             {// Japanese text like as follows
    301              // [Kanji][Kanji][Kana][Kanji][Kanji][Kana][Kana][Kana]
    302              "\u672C\u65E5(\u306F\u6674\u5929\u306A)\u308A\u3002",
    303              {
    304                      "\u672C\u65E5\u306F\u6674[(\u5929\u306A)\u308A\u3002]",
    305                      "\u672C\u65E5\u306F[(\u6674)]\u5929\u306A\u308A\u3002",
    306                      "\u672C[\u65E5(\u306F)]\u6674\u5929\u306A\u308A\u3002",
    307              }},
    308     };
    309 
    310     for (const auto& testCase : testCases) {
    311         auto[text, range] = parseTestString(testCase.testStr);
    312         uint32_t expectationIndex = 0;
    313         for (auto[acContext, acPiece] : LayoutSplitter(text, range, true /* isRtl */)) {
    314             ASSERT_NE(expectationIndex, testCase.expects.size());
    315             const std::string expectString = testCase.expects[expectationIndex++];
    316             auto[exContext, exPiece] = parseExpectString(expectString);
    317             EXPECT_EQ(acContext, exContext)
    318                     << expectString << " vs " << buildDebugString(text, acContext, acPiece);
    319             EXPECT_EQ(acPiece, exPiece)
    320                     << expectString << " vs " << buildDebugString(text, acContext, acPiece);
    321         }
    322         EXPECT_EQ(expectationIndex, testCase.expects.size()) << "Expectations Remains";
    323     }
    324 }
    325 
    326 }  // namespace
    327 }  // namespace minikin
    328