Commit b40da6df authored by Etienne Bergeron's avatar Etienne Bergeron Committed by Commit Bot

Lift fallback font unittests to share them across platforms

This CL is lifting the parameterized unittests originally
wrote for windows to a common file to be available to
other platforms.

Test from KnownExpectedFonts will now also run on Linux.
  * Added the test cases for linux.
Test from Glyphs are now running on every platforms.

Adding a matching_glyphs count to font_fallback_linux.cc.
Previously, a default font was returned for a single character
even if it was not matching the character, it was still the
best match. If no codepoints are found, the font is considered
a no match.

Change-Id: Ie4f871281737e254bbdbea9e04f8e527f1049569
Fixed: 3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1879956
Commit-Queue: Etienne Bergeron <etienneb@chromium.org>
Reviewed-by: default avatarAlexei Svitkine <asvitkine@chromium.org>
Reviewed-by: default avatarRobert Liao <robliao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#710364}
parent 6b95460e
......@@ -625,6 +625,8 @@ jumbo_static_library("test_support") {
"image/image_unittest_util.h",
"image/image_unittest_util_ios.mm",
"image/image_unittest_util_mac.mm",
"test/font_fallback_test_data.cc",
"test/font_fallback_test_data.h",
"test/gfx_util.cc",
"test/gfx_util.h",
"test/icc_profiles.cc",
......@@ -649,13 +651,6 @@ jumbo_static_library("test_support") {
deps += [ "//third_party:freetype_harfbuzz" ]
}
if (is_win) {
sources += [
"test/font_fallback_test_data.cc",
"test/font_fallback_test_data.h",
]
}
}
test("gfx_unittests") {
......@@ -702,6 +697,8 @@ test("gfx_unittests") {
"color_utils_unittest.cc",
"font_fallback_linux_unittest.cc",
"font_fallback_mac_unittest.cc",
"font_fallback_unittest.cc",
"font_fallback_win_unittest.cc",
"font_list_unittest.cc",
"geometry/axis_transform2d_unittest.cc",
"geometry/box_unittest.cc",
......@@ -730,6 +727,7 @@ test("gfx_unittests") {
"paint_vector_icon_unittest.cc",
"path_mac_unittest.mm",
"platform_font_mac_unittest.mm",
"platform_font_win_unittest.cc",
"range/range_mac_unittest.mm",
"range/range_unittest.cc",
"range/range_win_unittest.cc",
......@@ -740,6 +738,7 @@ test("gfx_unittests") {
"skia_util_unittest.cc",
"skia_vector_animation_unittest.cc",
"skrect_conversion_unittest.cc",
"system_fonts_win_unittest.cc",
"transform_util_unittest.cc",
"utf16_indexing_unittest.cc",
]
......@@ -808,13 +807,10 @@ test("gfx_unittests") {
if (is_win) {
sources += [
"font_fallback_win_unittest.cc",
"icon_util_unittest.cc",
"icon_util_unittests.rc",
"icon_util_unittests_resource.h",
"path_win_unittest.cc",
"platform_font_win_unittest.cc",
"system_fonts_win_unittest.cc",
"win/crash_id_helper_unittest.cc",
"win/direct_write_unittest.cc",
"win/text_analysis_source_unittest.cc",
......
......@@ -298,15 +298,19 @@ bool GetFallbackFont(const Font& font,
for (const auto& entry : cache_entry->second) {
// Validate that every character has a known glyph in the font.
size_t missing_glyphs = 0;
size_t matching_glyphs = 0;
size_t i = 0;
while (i < text.length()) {
UChar32 c = 0;
U16_NEXT(text.data(), i, text.length(), c);
if (!entry.HasGlyphForCharacter(c))
if (entry.HasGlyphForCharacter(c)) {
++matching_glyphs;
} else {
++missing_glyphs;
}
}
if (missing_glyphs < fewest_missing_glyphs) {
if (matching_glyphs > 0 && missing_glyphs < fewest_missing_glyphs) {
fewest_missing_glyphs = missing_glyphs;
prefered_entry = &entry;
}
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/gfx/font_fallback_win.h"
#include <tuple>
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/icu/source/common/unicode/uchar.h"
#include "third_party/icu/source/common/unicode/uscript.h"
#include "third_party/icu/source/common/unicode/utf16.h"
#include "third_party/skia/include/core/SkTypeface.h"
#include "ui/gfx/platform_font.h"
#include "ui/gfx/test/font_fallback_test_data.h"
#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif
namespace gfx {
namespace {
// Options to parameterized unittests.
struct FallbackFontTestOption {
bool ignore_get_fallback_failure = false;
bool skip_code_point_validation = false;
bool skip_fallback_fonts_validation = false;
};
const FallbackFontTestOption default_fallback_option = {false, false, false};
// Options for tests that does not validate the GetFallbackFont(...) parameters.
const FallbackFontTestOption untested_fallback_option = {true, true, true};
using FallbackFontTestParamInfo =
std::tuple<FallbackFontTestCase, FallbackFontTestOption>;
class GetFallbackFontTest
: public ::testing::TestWithParam<FallbackFontTestParamInfo> {
public:
GetFallbackFontTest() = default;
static std::string ParamInfoToString(
::testing::TestParamInfo<FallbackFontTestParamInfo> param_info) {
const FallbackFontTestCase& test_case = std::get<0>(param_info.param);
std::string language_tag = test_case.language_tag;
base::RemoveChars(language_tag, "-", &language_tag);
return std::string("S") + uscript_getName(test_case.script) + "L" +
language_tag;
}
void SetUp() override { std::tie(test_case_, test_option_) = GetParam(); }
protected:
bool GetFallbackFont(const Font& font,
const std::string& language_tag,
Font* result) {
return gfx::GetFallbackFont(font, language_tag, test_case_.text, result);
}
bool EnsuresScriptSupportCodePoints(const base::string16& text,
UScriptCode script,
const std::string& script_name) {
size_t i = 0;
while (i < text.length()) {
UChar32 code_point;
U16_NEXT(text.c_str(), i, text.size(), code_point);
if (!uscript_hasScript(code_point, script)) {
// Retrieve the appropriate script
UErrorCode script_error;
UScriptCode codepoint_script =
uscript_getScript(code_point, &script_error);
ADD_FAILURE() << "CodePoint U+" << std::hex << code_point
<< " is not part of the script '" << script_name
<< "'. Script '" << uscript_getName(codepoint_script)
<< "' detected.";
return false;
}
}
return true;
}
bool DoesFontSupportCodePoints(Font font, const base::string16& text) {
sk_sp<SkTypeface> skia_face =
font.platform_font()->GetNativeSkTypefaceIfAvailable();
if (!skia_face) {
skia_face =
SkTypeface::MakeFromName(font.GetFontName().c_str(), SkFontStyle());
}
if (!skia_face) {
LOG(ERROR) << "Cannot create typeface for '" << font.GetFontName()
<< "'.";
return false;
}
size_t i = 0;
const SkGlyphID kUnsupportedGlyph = 0;
while (i < text.length()) {
UChar32 code_point;
U16_NEXT(text.c_str(), i, text.size(), code_point);
SkGlyphID glyph_id = skia_face->unicharToGlyph(code_point);
if (glyph_id == kUnsupportedGlyph)
return false;
}
return true;
}
FallbackFontTestCase test_case_;
FallbackFontTestOption test_option_;
std::string script_name_;
private:
// Needed to bypass DCHECK in GetFallbackFont.
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::MainThreadType::UI};
DISALLOW_COPY_AND_ASSIGN(GetFallbackFontTest);
};
} // namespace
// This test ensures the font fallback work correctly. It will ensures that
// 1) The script supports the text
// 2) The input font does not already support the text
// 3) The call to GetFallbackFont() succeed
// 4) The fallback font has a glyph for every character of the text
//
// The previous checks can be activated or deactivated through the class
// FallbackFontTestOption (e.g. test_option_).
TEST_P(GetFallbackFontTest, GetFallbackFont) {
// Default system font.
const Font base_font;
#if defined(OS_WIN)
// Skip testing this call to GetFallbackFont on older windows versions. Some
// fonts only got introduced on windows 10 and the test will fail on previous
// versions.
const bool is_win10 = base::win::GetVersion() >= base::win::Version::WIN10;
if (test_case_.is_win10 && !is_win10)
return;
#endif
// Retrieve the name of the current script.
script_name_ = uscript_getName(test_case_.script);
// Validate that tested characters are part of the script.
if (!test_option_.skip_code_point_validation &&
!EnsuresScriptSupportCodePoints(test_case_.text, test_case_.script,
script_name_)) {
return;
}
// The default font already support it, do not try to find a fallback font.
if (DoesFontSupportCodePoints(base_font, test_case_.text))
return;
// Retrieve the fallback font.
Font fallback_font;
bool result =
GetFallbackFont(base_font, test_case_.language_tag, &fallback_font);
if (!result) {
if (!test_option_.ignore_get_fallback_failure)
ADD_FAILURE() << "GetFallbackFont failed for '" << script_name_ << "'";
return;
}
// Ensure the fallback font is a part of the validation fallback fonts list.
if (!test_option_.skip_fallback_fonts_validation) {
bool valid = std::find(test_case_.fallback_fonts.begin(),
test_case_.fallback_fonts.end(),
fallback_font.GetFontName()) !=
test_case_.fallback_fonts.end();
if (!valid) {
ADD_FAILURE() << "GetFallbackFont failed for '" << script_name_
<< "' invalid fallback font: "
<< fallback_font.GetFontName()
<< " not among valid options: "
<< base::JoinString(test_case_.fallback_fonts, ", ");
return;
}
}
// Ensure that glyphs exists in the fallback font.
if (!DoesFontSupportCodePoints(fallback_font, test_case_.text)) {
ADD_FAILURE() << "Font '" << fallback_font.GetFontName()
<< "' does not matched every CodePoints.";
return;
}
}
// Produces a font test case for every script.
std::vector<FallbackFontTestCase> GetSampleFontTestCases() {
std::vector<FallbackFontTestCase> result;
const unsigned int script_max = u_getIntPropertyMaxValue(UCHAR_SCRIPT) + 1;
for (unsigned int i = 0; i < script_max; i++) {
const UScriptCode script = static_cast<UScriptCode>(i);
// Make a sample text to test the script.
UChar text[8];
UErrorCode errorCode = U_ZERO_ERROR;
int text_length =
uscript_getSampleString(script, text, base::size(text), &errorCode);
if (text_length <= 0 || errorCode != U_ZERO_ERROR)
continue;
FallbackFontTestCase test_case(script, "", text, {});
result.push_back(test_case);
}
return result;
}
// Ensures that the default fallback font gives known results. The test
// is validating that a known fallback font is given for a given text and font.
INSTANTIATE_TEST_SUITE_P(
KnownExpectedFonts,
GetFallbackFontTest,
testing::Combine(testing::ValuesIn(kGetFontFallbackTests),
testing::Values(default_fallback_option)),
GetFallbackFontTest::ParamInfoToString);
// Ensures that font fallback functions are working properly for any string
// (strings from any script). The test doesn't enforce the functions to
// give a fallback font. The accepted behaviors are:
// 1) The fallback function failed and doesn't provide a fallback.
// 2) The fallback function succeeded and the font supports every glyphs.
INSTANTIATE_TEST_SUITE_P(
Glyphs,
GetFallbackFontTest,
testing::Combine(testing::ValuesIn(GetSampleFontTestCases()),
testing::Values(untested_fallback_option)),
GetFallbackFontTest::ParamInfoToString);
} // namespace gfx
This diff is collapsed.
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/gfx/test/font_fallback_test_data.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
namespace gfx {
#if defined(OS_WIN)
constexpr bool kWin10Only = true;
#endif
FallbackFontTestCase::FallbackFontTestCase() = default;
FallbackFontTestCase::FallbackFontTestCase(const FallbackFontTestCase& other) =
......@@ -26,6 +31,7 @@ FallbackFontTestCase::FallbackFontTestCase(
FallbackFontTestCase::~FallbackFontTestCase() = default;
#if defined(OS_WIN)
// A list of script and the fallback font on a default windows installation.
// This list may need to be updated if fonts or operating systems are
// upgraded.
......@@ -86,7 +92,6 @@ std::vector<FallbackFontTestCase> kGetFontFallbackTests = {
{"Segoe UI Symbol"},
kWin10Only},
{USCRIPT_DEVANAGARI, "hi", L"\u0905\u0906", {"Mangal", "Nirmala UI"}},
{USCRIPT_ETHIOPIC, "am", L"\u1201\u1202", {"Ebrima", "Nyala"}},
{USCRIPT_GEORGIAN,
"ka",
......@@ -245,4 +250,44 @@ std::vector<FallbackFontTestCase> kGetFontFallbackTests = {
{USCRIPT_VAI, "vai", L"\uA501\uA502", {"Ebrima"}},
{USCRIPT_YI, "yi", L"\uA000\uA001", {"Microsoft Yi Baiti"}}};
#elif defined(OS_LINUX)
// A list of script and the fallback font on the linux test environment.
// On linux, font-config configuration and fonts are mock. The config
// can be found in '${build}/etc/fonts/fonts.conf' and the test fonts
// can be found in '${build}/test_fonts/*'.
std::vector<FallbackFontTestCase> kGetFontFallbackTests = {
{USCRIPT_BENGALI,
"bn",
base::WideToUTF16(L"\u09B8\u09AE"),
{"Mukti Narrow"}},
{USCRIPT_DEVANAGARI,
"hi",
base::WideToUTF16(L"\u0905\u0906"),
{"Lohit Devanagari"}},
{USCRIPT_GURMUKHI,
"pa",
base::WideToUTF16(L"\u0A21\u0A22"),
{"Lohit Gurmukhi"}},
{USCRIPT_HAN, "zh-CN", base::WideToUTF16(L"\u6211"), {"Noto Sans CJK JP"}},
{USCRIPT_KHMER,
"km",
base::WideToUTF16(L"\u1780\u1781"),
{"Noto Sans Khmer"}},
{USCRIPT_TAMIL, "ta", base::WideToUTF16(L"\u0BB1\u0BB2"), {"Lohit Tamil"}},
{USCRIPT_THAI, "th", base::WideToUTF16(L"\u0e01\u0e02"), {"Garuda"}},
};
#else
// No fallback font tests are defined on that platform.
std::vector<FallbackFontTestCase> kGetFontFallbackTests = {};
#endif
} // namespace gfx
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment