Home | History | Annotate | Download | only in unittest
      1 /*
      2  * Copyright (C) 2016 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 "minikin/Layout.h"
     18 
     19 #include <gtest/gtest.h>
     20 
     21 #include "minikin/FontCollection.h"
     22 #include "minikin/LayoutPieces.h"
     23 
     24 #include "FontTestUtils.h"
     25 #include "UnicodeUtils.h"
     26 
     27 namespace minikin {
     28 
     29 static void expectAdvances(const std::vector<float>& expected, const std::vector<float>& advances) {
     30     EXPECT_LE(expected.size(), advances.size());
     31     for (size_t i = 0; i < expected.size(); ++i) {
     32         EXPECT_EQ(expected[i], advances[i])
     33                 << i << "th element is different. Expected: " << expected[i]
     34                 << ", Actual: " << advances[i];
     35     }
     36 }
     37 
     38 class LayoutTest : public testing::Test {
     39 protected:
     40     LayoutTest() : mCollection(nullptr) {}
     41 
     42     virtual ~LayoutTest() {}
     43 
     44     virtual void SetUp() override { mCollection = buildFontCollection("Ascii.ttf"); }
     45 
     46     virtual void TearDown() override {}
     47 
     48     std::shared_ptr<FontCollection> mCollection;
     49 };
     50 
     51 TEST_F(LayoutTest, doLayoutTest) {
     52     MinikinPaint paint(mCollection);
     53     paint.size = 10.0f;  // make 1em = 10px
     54     MinikinRect rect;
     55     std::vector<float> expectedValues;
     56 
     57     std::vector<uint16_t> text;
     58 
     59     // The mock implementation returns 10.0f advance and 0,0-10x10 bounds for all glyph.
     60     {
     61         SCOPED_TRACE("one word");
     62         text = utf8ToUtf16("oneword");
     63         Range range(0, text.size());
     64         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
     65                       EndHyphenEdit::NO_EDIT);
     66         EXPECT_EQ(70.0f, layout.getAdvance());
     67         layout.getBounds(&rect);
     68         EXPECT_EQ(0.0f, rect.mLeft);
     69         EXPECT_EQ(10.0f, rect.mTop);
     70         EXPECT_EQ(70.0f, rect.mRight);
     71         EXPECT_EQ(0.0f, rect.mBottom);
     72         expectedValues.resize(text.size());
     73         for (size_t i = 0; i < expectedValues.size(); ++i) {
     74             expectedValues[i] = 10.0f;
     75         }
     76         expectAdvances(expectedValues, layout.getAdvances());
     77     }
     78     {
     79         SCOPED_TRACE("two words");
     80         text = utf8ToUtf16("two words");
     81         Range range(0, text.size());
     82         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
     83                       EndHyphenEdit::NO_EDIT);
     84         EXPECT_EQ(90.0f, layout.getAdvance());
     85         layout.getBounds(&rect);
     86         EXPECT_EQ(0.0f, rect.mLeft);
     87         EXPECT_EQ(10.0f, rect.mTop);
     88         EXPECT_EQ(90.0f, rect.mRight);
     89         EXPECT_EQ(0.0f, rect.mBottom);
     90         expectedValues.resize(text.size());
     91         for (size_t i = 0; i < expectedValues.size(); ++i) {
     92             expectedValues[i] = 10.0f;
     93         }
     94         expectAdvances(expectedValues, layout.getAdvances());
     95     }
     96     {
     97         SCOPED_TRACE("three words");
     98         text = utf8ToUtf16("three words test");
     99         Range range(0, text.size());
    100         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    101                       EndHyphenEdit::NO_EDIT);
    102         EXPECT_EQ(160.0f, layout.getAdvance());
    103         layout.getBounds(&rect);
    104         EXPECT_EQ(0.0f, rect.mLeft);
    105         EXPECT_EQ(10.0f, rect.mTop);
    106         EXPECT_EQ(160.0f, rect.mRight);
    107         EXPECT_EQ(0.0f, rect.mBottom);
    108         expectedValues.resize(text.size());
    109         for (size_t i = 0; i < expectedValues.size(); ++i) {
    110             expectedValues[i] = 10.0f;
    111         }
    112         expectAdvances(expectedValues, layout.getAdvances());
    113     }
    114     {
    115         SCOPED_TRACE("two spaces");
    116         text = utf8ToUtf16("two  spaces");
    117         Range range(0, text.size());
    118         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    119                       EndHyphenEdit::NO_EDIT);
    120         EXPECT_EQ(110.0f, layout.getAdvance());
    121         layout.getBounds(&rect);
    122         EXPECT_EQ(0.0f, rect.mLeft);
    123         EXPECT_EQ(10.0f, rect.mTop);
    124         EXPECT_EQ(110.0f, rect.mRight);
    125         EXPECT_EQ(0.0f, rect.mBottom);
    126         expectedValues.resize(text.size());
    127         for (size_t i = 0; i < expectedValues.size(); ++i) {
    128             expectedValues[i] = 10.0f;
    129         }
    130         expectAdvances(expectedValues, layout.getAdvances());
    131     }
    132 }
    133 
    134 TEST_F(LayoutTest, doLayoutTest_wordSpacing) {
    135     MinikinPaint paint(mCollection);
    136     paint.size = 10.0f;  // make 1em = 10px
    137     MinikinRect rect;
    138     std::vector<float> expectedValues;
    139     std::vector<uint16_t> text;
    140 
    141     paint.wordSpacing = 5.0f;
    142 
    143     // The mock implementation returns 10.0f advance and 0,0-10x10 bounds for all glyph.
    144     {
    145         SCOPED_TRACE("one word");
    146         text = utf8ToUtf16("oneword");
    147         Range range(0, text.size());
    148         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    149                       EndHyphenEdit::NO_EDIT);
    150         EXPECT_EQ(70.0f, layout.getAdvance());
    151         layout.getBounds(&rect);
    152         EXPECT_EQ(0.0f, rect.mLeft);
    153         EXPECT_EQ(10.0f, rect.mTop);
    154         EXPECT_EQ(70.0f, rect.mRight);
    155         EXPECT_EQ(0.0f, rect.mBottom);
    156         expectedValues.resize(text.size());
    157         for (size_t i = 0; i < expectedValues.size(); ++i) {
    158             expectedValues[i] = 10.0f;
    159         }
    160         expectAdvances(expectedValues, layout.getAdvances());
    161     }
    162     {
    163         SCOPED_TRACE("two words");
    164         text = utf8ToUtf16("two words");
    165         Range range(0, text.size());
    166         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    167                       EndHyphenEdit::NO_EDIT);
    168         EXPECT_EQ(95.0f, layout.getAdvance());
    169         layout.getBounds(&rect);
    170         EXPECT_EQ(0.0f, rect.mLeft);
    171         EXPECT_EQ(10.0f, rect.mTop);
    172         EXPECT_EQ(95.0f, rect.mRight);
    173         EXPECT_EQ(0.0f, rect.mBottom);
    174         expectedValues.resize(text.size());
    175         for (size_t i = 0; i < expectedValues.size(); ++i) {
    176             expectedValues[i] = 10.0f;
    177         }
    178         expectedValues[3] = 15.0f;
    179         expectAdvances(expectedValues, layout.getAdvances());
    180     }
    181     {
    182         SCOPED_TRACE("three words test");
    183         text = utf8ToUtf16("three words test");
    184         Range range(0, text.size());
    185         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    186                       EndHyphenEdit::NO_EDIT);
    187         EXPECT_EQ(170.0f, layout.getAdvance());
    188         layout.getBounds(&rect);
    189         EXPECT_EQ(0.0f, rect.mLeft);
    190         EXPECT_EQ(10.0f, rect.mTop);
    191         EXPECT_EQ(170.0f, rect.mRight);
    192         EXPECT_EQ(0.0f, rect.mBottom);
    193         expectedValues.resize(text.size());
    194         for (size_t i = 0; i < expectedValues.size(); ++i) {
    195             expectedValues[i] = 10.0f;
    196         }
    197         expectedValues[5] = 15.0f;
    198         expectedValues[11] = 15.0f;
    199         expectAdvances(expectedValues, layout.getAdvances());
    200     }
    201     {
    202         SCOPED_TRACE("two spaces");
    203         text = utf8ToUtf16("two  spaces");
    204         Range range(0, text.size());
    205         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    206                       EndHyphenEdit::NO_EDIT);
    207         EXPECT_EQ(120.0f, layout.getAdvance());
    208         layout.getBounds(&rect);
    209         EXPECT_EQ(0.0f, rect.mLeft);
    210         EXPECT_EQ(10.0f, rect.mTop);
    211         EXPECT_EQ(120.0f, rect.mRight);
    212         EXPECT_EQ(0.0f, rect.mBottom);
    213         expectedValues.resize(text.size());
    214         for (size_t i = 0; i < expectedValues.size(); ++i) {
    215             expectedValues[i] = 10.0f;
    216         }
    217         expectedValues[3] = 15.0f;
    218         expectedValues[4] = 15.0f;
    219         expectAdvances(expectedValues, layout.getAdvances());
    220     }
    221 }
    222 
    223 TEST_F(LayoutTest, doLayoutTest_negativeWordSpacing) {
    224     MinikinPaint paint(mCollection);
    225     paint.size = 10.0f;  // make 1em = 10px
    226     MinikinRect rect;
    227     std::vector<float> expectedValues;
    228 
    229     std::vector<uint16_t> text;
    230 
    231     // Negative word spacing also should work.
    232     paint.wordSpacing = -5.0f;
    233 
    234     {
    235         SCOPED_TRACE("one word");
    236         text = utf8ToUtf16("oneword");
    237         Range range(0, text.size());
    238         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    239                       EndHyphenEdit::NO_EDIT);
    240         EXPECT_EQ(70.0f, layout.getAdvance());
    241         layout.getBounds(&rect);
    242         EXPECT_EQ(0.0f, rect.mLeft);
    243         EXPECT_EQ(10.0f, rect.mTop);
    244         EXPECT_EQ(70.0f, rect.mRight);
    245         EXPECT_EQ(0.0f, rect.mBottom);
    246         expectedValues.resize(text.size());
    247         for (size_t i = 0; i < expectedValues.size(); ++i) {
    248             expectedValues[i] = 10.0f;
    249         }
    250         expectAdvances(expectedValues, layout.getAdvances());
    251     }
    252     {
    253         SCOPED_TRACE("two words");
    254         text = utf8ToUtf16("two words");
    255         Range range(0, text.size());
    256         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    257                       EndHyphenEdit::NO_EDIT);
    258         EXPECT_EQ(85.0f, layout.getAdvance());
    259         layout.getBounds(&rect);
    260         EXPECT_EQ(0.0f, rect.mLeft);
    261         EXPECT_EQ(10.0f, rect.mTop);
    262         EXPECT_EQ(85.0f, rect.mRight);
    263         EXPECT_EQ(0.0f, rect.mBottom);
    264         expectedValues.resize(text.size());
    265         for (size_t i = 0; i < expectedValues.size(); ++i) {
    266             expectedValues[i] = 10.0f;
    267         }
    268         expectedValues[3] = 5.0f;
    269         expectAdvances(expectedValues, layout.getAdvances());
    270     }
    271     {
    272         SCOPED_TRACE("three words");
    273         text = utf8ToUtf16("three word test");
    274         Range range(0, text.size());
    275         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    276                       EndHyphenEdit::NO_EDIT);
    277         EXPECT_EQ(140.0f, layout.getAdvance());
    278         layout.getBounds(&rect);
    279         EXPECT_EQ(0.0f, rect.mLeft);
    280         EXPECT_EQ(10.0f, rect.mTop);
    281         EXPECT_EQ(140.0f, rect.mRight);
    282         EXPECT_EQ(0.0f, rect.mBottom);
    283         expectedValues.resize(text.size());
    284         for (size_t i = 0; i < expectedValues.size(); ++i) {
    285             expectedValues[i] = 10.0f;
    286         }
    287         expectedValues[5] = 5.0f;
    288         expectedValues[10] = 5.0f;
    289         expectAdvances(expectedValues, layout.getAdvances());
    290     }
    291     {
    292         SCOPED_TRACE("two spaces");
    293         text = utf8ToUtf16("two  spaces");
    294         Range range(0, text.size());
    295         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    296                       EndHyphenEdit::NO_EDIT);
    297         EXPECT_EQ(100.0f, layout.getAdvance());
    298         layout.getBounds(&rect);
    299         EXPECT_EQ(0.0f, rect.mLeft);
    300         EXPECT_EQ(10.0f, rect.mTop);
    301         EXPECT_EQ(100.0f, rect.mRight);
    302         EXPECT_EQ(0.0f, rect.mBottom);
    303         expectedValues.resize(text.size());
    304         for (size_t i = 0; i < expectedValues.size(); ++i) {
    305             expectedValues[i] = 10.0f;
    306         }
    307         expectedValues[3] = 5.0f;
    308         expectedValues[4] = 5.0f;
    309         expectAdvances(expectedValues, layout.getAdvances());
    310     }
    311 }
    312 
    313 // Test that a forced-RTL layout correctly mirros a forced-LTR layout.
    314 TEST_F(LayoutTest, doLayoutTest_rtlTest) {
    315     MinikinPaint paint(mCollection);
    316 
    317     std::vector<uint16_t> text = parseUnicodeString("'a' 'b' U+3042 U+3043 'c' 'd'");
    318     Range range(0, text.size());
    319 
    320     Layout ltrLayout(text, range, Bidi::FORCE_LTR, paint, StartHyphenEdit::NO_EDIT,
    321                      EndHyphenEdit::NO_EDIT);
    322 
    323     Layout rtlLayout(text, range, Bidi::FORCE_RTL, paint, StartHyphenEdit::NO_EDIT,
    324                      EndHyphenEdit::NO_EDIT);
    325 
    326     ASSERT_EQ(ltrLayout.nGlyphs(), rtlLayout.nGlyphs());
    327     ASSERT_EQ(6u, ltrLayout.nGlyphs());
    328 
    329     size_t nGlyphs = ltrLayout.nGlyphs();
    330     for (size_t i = 0; i < nGlyphs; ++i) {
    331         EXPECT_EQ(ltrLayout.getFont(i), rtlLayout.getFont(nGlyphs - i - 1));
    332         EXPECT_EQ(ltrLayout.getGlyphId(i), rtlLayout.getGlyphId(nGlyphs - i - 1));
    333     }
    334 }
    335 
    336 // Test that single-run RTL layouts of LTR-only text is laid out identical to an LTR layout.
    337 TEST_F(LayoutTest, singleRunBidiTest) {
    338     MinikinPaint paint(mCollection);
    339 
    340     std::vector<uint16_t> text = parseUnicodeString("'1' '2' '3'");
    341     Range range(0, text.size());
    342 
    343     Layout ltrLayout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    344                      EndHyphenEdit::NO_EDIT);
    345 
    346     Layout rtlLayout(text, range, Bidi::RTL, paint, StartHyphenEdit::NO_EDIT,
    347                      EndHyphenEdit::NO_EDIT);
    348 
    349     Layout defaultRtlLayout(text, range, Bidi::DEFAULT_RTL, paint, StartHyphenEdit::NO_EDIT,
    350                             EndHyphenEdit::NO_EDIT);
    351 
    352     const size_t nGlyphs = ltrLayout.nGlyphs();
    353     ASSERT_EQ(3u, nGlyphs);
    354 
    355     ASSERT_EQ(nGlyphs, rtlLayout.nGlyphs());
    356     ASSERT_EQ(nGlyphs, defaultRtlLayout.nGlyphs());
    357 
    358     for (size_t i = 0; i < nGlyphs; ++i) {
    359         EXPECT_EQ(ltrLayout.getFont(i), rtlLayout.getFont(i));
    360         EXPECT_EQ(ltrLayout.getGlyphId(i), rtlLayout.getGlyphId(i));
    361         EXPECT_EQ(ltrLayout.getFont(i), defaultRtlLayout.getFont(i));
    362         EXPECT_EQ(ltrLayout.getGlyphId(i), defaultRtlLayout.getGlyphId(i));
    363     }
    364 }
    365 
    366 TEST_F(LayoutTest, hyphenationTest) {
    367     MinikinPaint paint(mCollection);
    368     paint.size = 10.0f;  // make 1em = 10px
    369     std::vector<uint16_t> text;
    370 
    371     // The mock implementation returns 10.0f advance for all glyphs.
    372     {
    373         SCOPED_TRACE("one word with no hyphen edit");
    374         text = utf8ToUtf16("oneword");
    375         Range range(0, text.size());
    376         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    377                       EndHyphenEdit::NO_EDIT);
    378         EXPECT_EQ(70.0f, layout.getAdvance());
    379     }
    380     {
    381         SCOPED_TRACE("one word with hyphen insertion at the end");
    382         text = utf8ToUtf16("oneword");
    383         Range range(0, text.size());
    384         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    385                       EndHyphenEdit::INSERT_HYPHEN);
    386         EXPECT_EQ(80.0f, layout.getAdvance());
    387     }
    388     {
    389         SCOPED_TRACE("one word with hyphen replacement at the end");
    390         text = utf8ToUtf16("oneword");
    391         Range range(0, text.size());
    392         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    393                       EndHyphenEdit::REPLACE_WITH_HYPHEN);
    394         EXPECT_EQ(70.0f, layout.getAdvance());
    395     }
    396     {
    397         SCOPED_TRACE("one word with hyphen insertion at the start");
    398         text = utf8ToUtf16("oneword");
    399         Range range(0, text.size());
    400         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::INSERT_HYPHEN,
    401                       EndHyphenEdit::NO_EDIT);
    402         EXPECT_EQ(80.0f, layout.getAdvance());
    403     }
    404     {
    405         SCOPED_TRACE("one word with hyphen insertion at the both ends");
    406         text = utf8ToUtf16("oneword");
    407         Range range(0, text.size());
    408         Layout layout(text, range, Bidi::LTR, paint, StartHyphenEdit::INSERT_HYPHEN,
    409                       EndHyphenEdit::INSERT_HYPHEN);
    410         EXPECT_EQ(90.0f, layout.getAdvance());
    411     }
    412 }
    413 
    414 TEST_F(LayoutTest, measuredTextTest) {
    415     // The test font has following coverage and width.
    416     // U+0020: 10em
    417     // U+002E (.): 10em
    418     // U+0043 (C): 100em
    419     // U+0049 (I): 1em
    420     // U+004C (L): 50em
    421     // U+0056 (V): 5em
    422     // U+0058 (X): 10em
    423     // U+005F (_): 0em
    424     // U+FFFD (invalid surrogate will be replaced to this): 7em
    425     // U+10331 (\uD800\uDF31): 10em
    426     auto fc = buildFontCollection("LayoutTestFont.ttf");
    427     {
    428         MinikinPaint paint(fc);
    429         std::vector<uint16_t> text = utf8ToUtf16("I");
    430         std::vector<float> advances(text.size());
    431         Range range(0, text.size());
    432         EXPECT_EQ(1.0f, Layout::measureText(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    433                                             EndHyphenEdit::NO_EDIT, advances.data()));
    434         ASSERT_EQ(1u, advances.size());
    435         EXPECT_EQ(1.0f, advances[0]);
    436     }
    437     {
    438         MinikinPaint paint(fc);
    439         std::vector<uint16_t> text = utf8ToUtf16("IV");
    440         std::vector<float> advances(text.size());
    441         Range range(0, text.size());
    442         EXPECT_EQ(6.0f, Layout::measureText(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    443                                             EndHyphenEdit::NO_EDIT, advances.data()));
    444         ASSERT_EQ(2u, advances.size());
    445         EXPECT_EQ(1.0f, advances[0]);
    446         EXPECT_EQ(5.0f, advances[1]);
    447     }
    448     {
    449         MinikinPaint paint(fc);
    450         std::vector<uint16_t> text = utf8ToUtf16("IVX");
    451         std::vector<float> advances(text.size());
    452         Range range(0, text.size());
    453         EXPECT_EQ(16.0f,
    454                   Layout::measureText(text, range, Bidi::LTR, paint, StartHyphenEdit::NO_EDIT,
    455                                       EndHyphenEdit::NO_EDIT, advances.data()));
    456         ASSERT_EQ(3u, advances.size());
    457         EXPECT_EQ(1.0f, advances[0]);
    458         EXPECT_EQ(5.0f, advances[1]);
    459         EXPECT_EQ(10.0f, advances[2]);
    460     }
    461 }
    462 
    463 // TODO: Add more test cases, e.g. measure text, letter spacing.
    464 
    465 }  // namespace minikin
    466