gecko-dev/intl/components/gtest/TestBidi.cpp

315 строки
9.9 KiB
C++
Исходник Ответственный История

Этот файл содержит неоднозначные символы Юникода!

Этот файл содержит неоднозначные символы Юникода, которые могут быть перепутаны с другими в текущей локали. Если это намеренно, можете спокойно проигнорировать это предупреждение. Используйте кнопку Экранировать, чтобы подсветить эти символы.

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h"
#include "mozilla/intl/Bidi.h"
#include "mozilla/Span.h"
namespace mozilla::intl {
struct VisualRun {
Span<const char16_t> string;
BidiDirection direction;
};
/**
* An iterator for visual runs in a paragraph. See Bug 1736597 for integrating
* this into the public API.
*/
class MOZ_STACK_CLASS VisualRunIter {
public:
VisualRunIter(Bidi& aBidi, Span<const char16_t> aParagraph,
BidiEmbeddingLevel aLevel)
: mBidi(aBidi), mParagraph(aParagraph) {
// Crash in case of errors by calling unwrap. If this were a real API, this
// would be a TryCreate call.
mBidi.SetParagraph(aParagraph, aLevel).unwrap();
mRunCount = mBidi.CountRuns().unwrap();
}
Maybe<VisualRun> Next() {
if (mRunIndex >= mRunCount) {
return Nothing();
}
int32_t stringIndex = -1;
int32_t stringLength = -1;
BidiDirection direction =
mBidi.GetVisualRun(mRunIndex, &stringIndex, &stringLength);
Span<const char16_t> string(mParagraph.Elements() + stringIndex,
stringLength);
mRunIndex++;
return Some(VisualRun{string, direction});
}
private:
Bidi& mBidi;
Span<const char16_t> mParagraph = Span<const char16_t>();
int32_t mRunIndex = 0;
int32_t mRunCount = 0;
};
struct LogicalRun {
Span<const char16_t> string;
BidiEmbeddingLevel embeddingLevel;
};
/**
* An iterator for logical runs in a paragraph. See Bug 1736597 for integrating
* this into the public API.
*/
class MOZ_STACK_CLASS LogicalRunIter {
public:
LogicalRunIter(Bidi& aBidi, Span<const char16_t> aParagraph,
BidiEmbeddingLevel aLevel)
: mBidi(aBidi), mParagraph(aParagraph) {
// Crash in case of errors by calling unwrap. If this were a real API, this
// would be a TryCreate call.
mBidi.SetParagraph(aParagraph, aLevel).unwrap();
mBidi.CountRuns().unwrap();
}
Maybe<LogicalRun> Next() {
if (mRunIndex >= static_cast<int32_t>(mParagraph.Length())) {
return Nothing();
}
int32_t logicalLimit;
BidiEmbeddingLevel embeddingLevel;
mBidi.GetLogicalRun(mRunIndex, &logicalLimit, &embeddingLevel);
Span<const char16_t> string(mParagraph.Elements() + mRunIndex,
logicalLimit - mRunIndex);
mRunIndex = logicalLimit;
return Some(LogicalRun{string, embeddingLevel});
}
private:
Bidi& mBidi;
Span<const char16_t> mParagraph = Span<const char16_t>();
int32_t mRunIndex = 0;
};
TEST(IntlBidi, SimpleLTR)
{
Bidi bidi{};
LogicalRunIter logicalRunIter(bidi, MakeStringSpan(u"this is a paragraph"),
BidiEmbeddingLevel::DefaultLTR());
ASSERT_EQ(bidi.GetParagraphEmbeddingLevel(), 0);
ASSERT_EQ(bidi.GetParagraphDirection(), Bidi::ParagraphDirection::LTR);
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"this is a paragraph"));
ASSERT_EQ(logicalRun->embeddingLevel, 0);
ASSERT_EQ(logicalRun->embeddingLevel.Direction(), BidiDirection::LTR);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isNothing());
}
}
TEST(IntlBidi, SimpleRTL)
{
Bidi bidi{};
LogicalRunIter logicalRunIter(bidi, MakeStringSpan(u"فايرفوكس رائع"),
BidiEmbeddingLevel::DefaultLTR());
ASSERT_EQ(bidi.GetParagraphEmbeddingLevel(), 1);
ASSERT_EQ(bidi.GetParagraphDirection(), Bidi::ParagraphDirection::RTL);
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"فايرفوكس رائع"));
ASSERT_EQ(logicalRun->embeddingLevel.Direction(), BidiDirection::RTL);
ASSERT_EQ(logicalRun->embeddingLevel, 1);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isNothing());
}
}
TEST(IntlBidi, MultiLevel)
{
Bidi bidi{};
LogicalRunIter logicalRunIter(
bidi, MakeStringSpan(u"Firefox is awesome: رائع Firefox"),
BidiEmbeddingLevel::DefaultLTR());
ASSERT_EQ(bidi.GetParagraphEmbeddingLevel(), 0);
ASSERT_EQ(bidi.GetParagraphDirection(), Bidi::ParagraphDirection::Mixed);
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"Firefox is awesome: "));
ASSERT_EQ(logicalRun->embeddingLevel, 0);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"رائع"));
ASSERT_EQ(logicalRun->embeddingLevel, 1);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u" Firefox"));
ASSERT_EQ(logicalRun->embeddingLevel, 0);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isNothing());
}
}
TEST(IntlBidi, RtlOverride)
{
Bidi bidi{};
// Set the paragraph using the RTL embedding mark U+202B, and the LTR
// embedding mark U+202A to increase the embedding level. This mark switches
// the weakly directional character "_". This demonstrates that embedding
// levels can be computed.
LogicalRunIter logicalRunIter(
bidi, MakeStringSpan(u"ltr\u202b___رائع___\u202a___ltr__"),
BidiEmbeddingLevel::DefaultLTR());
ASSERT_EQ(bidi.GetParagraphEmbeddingLevel(), 0);
ASSERT_EQ(bidi.GetParagraphDirection(), Bidi::ParagraphDirection::Mixed);
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"ltr"));
ASSERT_EQ(logicalRun->embeddingLevel, 0);
ASSERT_EQ(logicalRun->embeddingLevel.Direction(), BidiDirection::LTR);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"\u202b___رائع___"));
ASSERT_EQ(logicalRun->embeddingLevel, 1);
ASSERT_EQ(logicalRun->embeddingLevel.Direction(), BidiDirection::RTL);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isSome());
ASSERT_EQ(logicalRun->string, MakeStringSpan(u"\u202a___ltr__"));
ASSERT_EQ(logicalRun->embeddingLevel, 2);
ASSERT_EQ(logicalRun->embeddingLevel.Direction(), BidiDirection::LTR);
}
{
auto logicalRun = logicalRunIter.Next();
ASSERT_TRUE(logicalRun.isNothing());
}
}
TEST(IntlBidi, VisualRuns)
{
Bidi bidi{};
VisualRunIter visualRunIter(
bidi,
MakeStringSpan(
u"first visual run التشغيل البصري الثاني third visual run"),
BidiEmbeddingLevel::DefaultLTR());
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isSome());
ASSERT_EQ(run->string, MakeStringSpan(u"first visual run "));
ASSERT_EQ(run->direction, BidiDirection::LTR);
}
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isSome());
ASSERT_EQ(run->string, MakeStringSpan(u"التشغيل البصري الثاني"));
ASSERT_EQ(run->direction, BidiDirection::RTL);
}
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isSome());
ASSERT_EQ(run->string, MakeStringSpan(u" third visual run"));
ASSERT_EQ(run->direction, BidiDirection::LTR);
}
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isNothing());
}
}
TEST(IntlBidi, VisualRunsWithEmbeds)
{
// Compare this test to the logical order test.
Bidi bidi{};
VisualRunIter visualRunIter(
bidi, MakeStringSpan(u"ltr\u202b___رائع___\u202a___ltr___"),
BidiEmbeddingLevel::DefaultLTR());
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isSome());
ASSERT_EQ(run->string, MakeStringSpan(u"ltr"));
ASSERT_EQ(run->direction, BidiDirection::LTR);
}
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isSome());
ASSERT_EQ(run->string, MakeStringSpan(u"\u202a___ltr___"));
ASSERT_EQ(run->direction, BidiDirection::LTR);
}
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isSome());
ASSERT_EQ(run->string, MakeStringSpan(u"\u202b___رائع___"));
ASSERT_EQ(run->direction, BidiDirection::RTL);
}
{
Maybe<VisualRun> run = visualRunIter.Next();
ASSERT_TRUE(run.isNothing());
}
}
// The full Bidi class can be found in [1].
//
// [1]: https://www.unicode.org/Public/UNIDATA/extracted/DerivedBidiClass.txt
TEST(IntlBidi, GetBaseDirection)
{
// Return Neutral as default if empty string is provided.
ASSERT_EQ(Bidi::GetBaseDirection(nullptr), Bidi::BaseDirection::Neutral);
// White space(WS) is classified as Neutral.
ASSERT_EQ(Bidi::GetBaseDirection(MakeStringSpan(u" ")),
Bidi::BaseDirection::Neutral);
// 000A and 000D are paragraph separators(BS), which are also classified as
// Neutral.
ASSERT_EQ(Bidi::GetBaseDirection(MakeStringSpan(u"\u000A")),
Bidi::BaseDirection::Neutral);
ASSERT_EQ(Bidi::GetBaseDirection(MakeStringSpan(u"\u000D")),
Bidi::BaseDirection::Neutral);
// 0620..063f are Arabic letters, which is of type AL.
ASSERT_EQ(Bidi::GetBaseDirection(MakeStringSpan(u"\u0620\u0621\u0622")),
Bidi::BaseDirection::RTL);
ASSERT_EQ(Bidi::GetBaseDirection(MakeStringSpan(u" \u0620\u0621\u0622")),
Bidi::BaseDirection::RTL);
ASSERT_EQ(Bidi::GetBaseDirection(MakeStringSpan(u"\u0620\u0621\u0622ABC")),
Bidi::BaseDirection::RTL);
// First strong character is of English letters.
ASSERT_EQ(Bidi::GetBaseDirection(MakeStringSpan(u"ABC")),
Bidi::BaseDirection::LTR);
ASSERT_EQ(Bidi::GetBaseDirection(MakeStringSpan(u" ABC")),
Bidi::BaseDirection::LTR);
ASSERT_EQ(Bidi::GetBaseDirection(MakeStringSpan(u"ABC\u0620")),
Bidi::BaseDirection::LTR);
}
} // namespace mozilla::intl