blob: 3d9d3b19d8416948559f25f16cdb6f28c2ec69ed [file] [log] [blame]
// Copyright 2016 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 "platform/text/Hyphenation.h"
#include "build/build_config.h"
#include "platform/LayoutLocale.h"
#include "platform/fonts/FontGlobalContext.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::ElementsAre;
using testing::ElementsAreArray;
#if defined(OS_ANDROID)
#define USE_MINIKIN_HYPHENATION
#endif
#if defined(USE_MINIKIN_HYPHENATION)
#include "base/files/file_path.h"
#include "platform/text/hyphenation/HyphenationMinikin.h"
#endif
namespace blink {
class NoHyphenation : public Hyphenation {
public:
size_t LastHyphenLocation(const StringView&,
size_t before_index) const override {
return 0;
}
};
class HyphenationTest : public ::testing::Test {
protected:
void TearDown() override { FontGlobalContext::ClearForTesting(); }
#if defined(USE_MINIKIN_HYPHENATION) || defined(OS_MACOSX)
// Get a |Hyphenation| instnace for the specified locale for testing.
scoped_refptr<Hyphenation> GetHyphenation(const AtomicString& locale) {
#if defined(USE_MINIKIN_HYPHENATION)
// Because the mojo service to open hyphenation dictionaries is not
// accessible from the unit test, open the dictionary file directly for
// testing.
std::string filename =
std::string("hyph-") + locale.Ascii().data() + ".hyb";
#if defined(OS_ANDROID)
base::FilePath path("/system/usr/hyphen-data");
#else
#error "This configuration is not supported."
#endif
path = path.AppendASCII(filename);
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (file.IsValid())
return HyphenationMinikin::FromFileForTesting(std::move(file));
#else
if (const LayoutLocale* layout_locale = LayoutLocale::Get(locale))
return layout_locale->GetHyphenation();
#endif
return nullptr;
}
#endif
};
TEST_F(HyphenationTest, Get) {
scoped_refptr<Hyphenation> hyphenation = base::AdoptRef(new NoHyphenation);
LayoutLocale::SetHyphenationForTesting("en-US", hyphenation);
EXPECT_EQ(hyphenation.get(), LayoutLocale::Get("en-US")->GetHyphenation());
LayoutLocale::SetHyphenationForTesting("en-UK", nullptr);
EXPECT_EQ(nullptr, LayoutLocale::Get("en-UK")->GetHyphenation());
}
#if defined(USE_MINIKIN_HYPHENATION) || defined(OS_MACOSX)
TEST_F(HyphenationTest, HyphenLocations) {
scoped_refptr<Hyphenation> hyphenation = GetHyphenation("en-us");
ASSERT_TRUE(hyphenation) << "Cannot find the hyphenation engine";
// Get all hyphenation points by |HyphenLocations|.
const String word("hyphenation");
Vector<size_t, 8> locations = hyphenation->HyphenLocations(word);
EXPECT_GT(locations.size(), 0u);
for (unsigned i = 1; i < locations.size(); i++) {
ASSERT_GT(locations[i - 1], locations[i])
<< "hyphenLocations must return locations in the descending order";
}
// Test |LastHyphenLocation| returns all hyphenation points.
Vector<size_t, 8> actual;
for (unsigned offset = word.length();;) {
offset = hyphenation->LastHyphenLocation(word, offset);
if (!offset)
break;
actual.push_back(offset);
}
EXPECT_THAT(actual, ElementsAreArray(locations));
// Test |FirstHyphenLocation| returns all hyphenation points.
actual.clear();
for (unsigned offset = 0;;) {
offset = hyphenation->FirstHyphenLocation(word, offset);
if (!offset)
break;
actual.push_back(offset);
}
locations.Reverse();
EXPECT_THAT(actual, ElementsAreArray(locations));
}
TEST_F(HyphenationTest, LeadingSpaces) {
scoped_refptr<Hyphenation> hyphenation = GetHyphenation("en-us");
#if defined(OS_ANDROID)
// Hyphenation is available only for Android M MR1 or later.
if (!hyphenation)
return;
#endif
ASSERT_TRUE(hyphenation) << "Cannot find the hyphenation for en-us";
String leading_space(" principle");
EXPECT_THAT(hyphenation->HyphenLocations(leading_space), ElementsAre(7, 5));
EXPECT_EQ(5u, hyphenation->LastHyphenLocation(leading_space, 6));
String multi_leading_spaces(" principle");
EXPECT_THAT(hyphenation->HyphenLocations(multi_leading_spaces),
ElementsAre(9, 7));
EXPECT_EQ(7u, hyphenation->LastHyphenLocation(multi_leading_spaces, 8));
// Line breaker is not supposed to pass only spaces, no locations.
String only_spaces(" ");
EXPECT_THAT(hyphenation->HyphenLocations(only_spaces), ElementsAre());
EXPECT_EQ(0u, hyphenation->LastHyphenLocation(only_spaces, 3));
// Line breaker is not supposed to pass with trailing spaces.
String trailing_space("principle ");
EXPECT_THAT(hyphenation->HyphenLocations(trailing_space),
testing::AnyOf(ElementsAre(), ElementsAre(6, 4)));
EXPECT_EQ(0u, hyphenation->LastHyphenLocation(trailing_space, 10));
}
TEST_F(HyphenationTest, English) {
scoped_refptr<Hyphenation> hyphenation = GetHyphenation("en-us");
#if defined(OS_ANDROID)
// Hyphenation is available only for Android M MR1 or later.
if (!hyphenation)
return;
#endif
ASSERT_TRUE(hyphenation) << "Cannot find the hyphenation for en-us";
Vector<size_t, 8> locations = hyphenation->HyphenLocations("hyphenation");
EXPECT_THAT(locations, testing::AnyOf(ElementsAreArray({6, 2}),
ElementsAreArray({7, 6, 2})));
}
TEST_F(HyphenationTest, German) {
scoped_refptr<Hyphenation> hyphenation = GetHyphenation("de-1996");
#if defined(OS_ANDROID)
// Hyphenation is available only for Android M MR1 or later.
if (!hyphenation)
return;
#endif
ASSERT_TRUE(hyphenation) << "Cannot find the hyphenation for de-1996";
Vector<size_t, 8> locations = hyphenation->HyphenLocations("konsonantien");
EXPECT_THAT(locations, ElementsAreArray({8, 5, 3}));
// Test words with non-ASCII (> U+0080) characters.
locations = hyphenation->HyphenLocations(
"B"
"\xE4" // LATIN SMALL LETTER A WITH DIAERESIS
"chlein");
EXPECT_THAT(locations, ElementsAreArray({4}));
}
#endif
} // namespace blink