| /* |
| * 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 "minikin/Layout.h" |
| |
| #include <gtest/gtest.h> |
| |
| #include "minikin/LayoutCache.h" |
| |
| #include "FontTestUtils.h" |
| #include "LocaleListCache.h" |
| #include "UnicodeUtils.h" |
| |
| namespace minikin { |
| |
| class TestableLayoutCache : public LayoutCache { |
| public: |
| TestableLayoutCache(uint32_t maxEntries) : LayoutCache(maxEntries) {} |
| }; |
| |
| class LayoutCapture { |
| public: |
| LayoutCapture() {} |
| |
| void operator()(const Layout& layout) { mLayout = &layout; } |
| |
| const Layout* get() const { return mLayout; } |
| |
| private: |
| const Layout* mLayout; |
| }; |
| |
| TEST(LayoutCacheTest, cacheHitTest) { |
| auto text = utf8ToUtf16("android"); |
| Range range(0, text.size()); |
| MinikinPaint paint(buildFontCollection("Ascii.ttf")); |
| |
| TestableLayoutCache layoutCache(10); |
| |
| LayoutCapture layout1; |
| layoutCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT, |
| EndHyphenEdit::NO_EDIT, layout1); |
| |
| LayoutCapture layout2; |
| layoutCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT, |
| EndHyphenEdit::NO_EDIT, layout2); |
| |
| EXPECT_EQ(layout1.get(), layout2.get()); |
| } |
| |
| TEST(LayoutCacheTest, cacheMissTest) { |
| auto text1 = utf8ToUtf16("android"); |
| auto text2 = utf8ToUtf16("ANDROID"); |
| MinikinPaint paint(buildFontCollection("Ascii.ttf")); |
| |
| TestableLayoutCache layoutCache(10); |
| |
| LayoutCapture layout1; |
| LayoutCapture layout2; |
| |
| { |
| SCOPED_TRACE("Different text"); |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| layoutCache.getOrCreate(text2, Range(0, text2.size()), paint, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different range"); |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| layoutCache.getOrCreate(text1, Range(1, text1.size()), paint, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different text"); |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| layoutCache.getOrCreate(text2, Range(0, text2.size()), paint, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different direction"); |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, true /* RTL */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different start hyphenation"); |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */, |
| StartHyphenEdit::INSERT_HYPHEN, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different end hyphen"); |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::INSERT_HYPHEN, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different collection"); |
| MinikinPaint paint1(buildFontCollection("Ascii.ttf")); |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| MinikinPaint paint2(buildFontCollection("Emoji.ttf")); |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different size"); |
| auto collection = buildFontCollection("Ascii.ttf"); |
| MinikinPaint paint1(collection); |
| paint1.size = 10.0f; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| MinikinPaint paint2(collection); |
| paint2.size = 20.0f; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different scale X"); |
| auto collection = buildFontCollection("Ascii.ttf"); |
| MinikinPaint paint1(collection); |
| paint1.scaleX = 1.0f; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| MinikinPaint paint2(collection); |
| paint2.scaleX = 2.0f; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different skew X"); |
| auto collection = buildFontCollection("Ascii.ttf"); |
| MinikinPaint paint1(collection); |
| paint1.skewX = 1.0f; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| MinikinPaint paint2(collection); |
| paint2.skewX = 2.0f; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different letter spacing"); |
| auto collection = buildFontCollection("Ascii.ttf"); |
| MinikinPaint paint1(collection); |
| paint1.letterSpacing = 0.0f; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| MinikinPaint paint2(collection); |
| paint2.letterSpacing = 1.0f; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different word spacing"); |
| auto collection = buildFontCollection("Ascii.ttf"); |
| MinikinPaint paint1(collection); |
| paint1.wordSpacing = 0.0f; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| MinikinPaint paint2(collection); |
| paint2.wordSpacing = 1.0f; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different paint flags"); |
| auto collection = buildFontCollection("Ascii.ttf"); |
| MinikinPaint paint1(collection); |
| paint1.paintFlags = 0; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| MinikinPaint paint2(collection); |
| paint2.paintFlags = LinearTextFlag; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different locale list ID"); |
| auto collection = buildFontCollection("Ascii.ttf"); |
| MinikinPaint paint1(collection); |
| paint1.localeListId = LocaleListCache::getId("en-US"); |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| MinikinPaint paint2(collection); |
| paint2.localeListId = LocaleListCache::getId("ja-JP"); |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different family variant"); |
| auto collection = buildFontCollection("Ascii.ttf"); |
| MinikinPaint paint1(collection); |
| paint1.familyVariant = FontFamily::Variant::DEFAULT; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| MinikinPaint paint2(collection); |
| paint2.familyVariant = FontFamily::Variant::COMPACT; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| { |
| SCOPED_TRACE("Different font feature settings"); |
| auto collection = buildFontCollection("Ascii.ttf"); |
| MinikinPaint paint1(collection); |
| paint1.fontFeatureSettings = ""; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1); |
| MinikinPaint paint2(collection); |
| paint2.fontFeatureSettings = "'liga' on"; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| EXPECT_NE(layout1.get(), layout2.get()); |
| } |
| } |
| |
| TEST(LayoutCacheTest, cacheOverflowTest) { |
| auto text = utf8ToUtf16("android"); |
| Range range(0, text.size()); |
| MinikinPaint paint(buildFontCollection("Ascii.ttf")); |
| |
| TestableLayoutCache layoutCache(5); |
| |
| LayoutCapture layout1; |
| layoutCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT, |
| EndHyphenEdit::NO_EDIT, layout1); |
| |
| for (char c = 'a'; c <= 'z'; c++) { |
| auto text1 = utf8ToUtf16(std::string(c, 10)); |
| LayoutCapture layout2; |
| layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */, |
| StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2); |
| } |
| |
| LayoutCapture layout3; |
| layoutCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT, |
| EndHyphenEdit::NO_EDIT, layout3); |
| EXPECT_NE(layout1.get(), layout3.get()); |
| } |
| |
| } // namespace minikin |