1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/i18n/bidi_line_iterator.h"
6
7 #include "base/macros.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10
11 namespace base {
12 namespace i18n {
13 namespace {
14
15 class BiDiLineIteratorTest : public testing::TestWithParam<TextDirection> {
16 public:
17 BiDiLineIteratorTest() = default;
18
iterator()19 BiDiLineIterator* iterator() { return &iterator_; }
20
21 private:
22 BiDiLineIterator iterator_;
23
24 DISALLOW_COPY_AND_ASSIGN(BiDiLineIteratorTest);
25 };
26
TEST_P(BiDiLineIteratorTest,OnlyLTR)27 TEST_P(BiDiLineIteratorTest, OnlyLTR) {
28 iterator()->Open(UTF8ToUTF16("abc 测试"), GetParam(),
29 BiDiLineIterator::CustomBehavior::NONE);
30 ASSERT_EQ(1, iterator()->CountRuns());
31
32 int start, length;
33 EXPECT_EQ(UBIDI_LTR, iterator()->GetVisualRun(0, &start, &length));
34 EXPECT_EQ(0, start);
35 EXPECT_EQ(9, length);
36
37 int end;
38 UBiDiLevel level;
39 iterator()->GetLogicalRun(0, &end, &level);
40 EXPECT_EQ(9, end);
41 if (GetParam() == TextDirection::RIGHT_TO_LEFT)
42 EXPECT_EQ(2, level);
43 else
44 EXPECT_EQ(0, level);
45 }
46
TEST_P(BiDiLineIteratorTest,OnlyRTL)47 TEST_P(BiDiLineIteratorTest, OnlyRTL) {
48 iterator()->Open(UTF8ToUTF16("מה השעה"), GetParam(),
49 BiDiLineIterator::CustomBehavior::NONE);
50 ASSERT_EQ(1, iterator()->CountRuns());
51
52 int start, length;
53 EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(0, &start, &length));
54 EXPECT_EQ(0, start);
55 EXPECT_EQ(7, length);
56
57 int end;
58 UBiDiLevel level;
59 iterator()->GetLogicalRun(0, &end, &level);
60 EXPECT_EQ(7, end);
61 EXPECT_EQ(1, level);
62 }
63
TEST_P(BiDiLineIteratorTest,Mixed)64 TEST_P(BiDiLineIteratorTest, Mixed) {
65 iterator()->Open(UTF8ToUTF16("אני משתמש ב- Chrome כדפדפן האינטרנט שלי"),
66 GetParam(), BiDiLineIterator::CustomBehavior::NONE);
67 ASSERT_EQ(3, iterator()->CountRuns());
68
69 // We'll get completely different results depending on the top-level paragraph
70 // direction.
71 if (GetParam() == TextDirection::RIGHT_TO_LEFT) {
72 // If para direction is RTL, expect the LTR substring "Chrome" to be nested
73 // within the surrounding RTL text.
74 int start, length;
75 EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(0, &start, &length));
76 EXPECT_EQ(19, start);
77 EXPECT_EQ(20, length);
78 EXPECT_EQ(UBIDI_LTR, iterator()->GetVisualRun(1, &start, &length));
79 EXPECT_EQ(13, start);
80 EXPECT_EQ(6, length);
81 EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(2, &start, &length));
82 EXPECT_EQ(0, start);
83 EXPECT_EQ(13, length);
84
85 int end;
86 UBiDiLevel level;
87 iterator()->GetLogicalRun(0, &end, &level);
88 EXPECT_EQ(13, end);
89 EXPECT_EQ(1, level);
90 iterator()->GetLogicalRun(13, &end, &level);
91 EXPECT_EQ(19, end);
92 EXPECT_EQ(2, level);
93 iterator()->GetLogicalRun(19, &end, &level);
94 EXPECT_EQ(39, end);
95 EXPECT_EQ(1, level);
96 } else {
97 // If the para direction is LTR, expect the LTR substring "- Chrome " to be
98 // at the top level, with two nested RTL runs on either side.
99 int start, length;
100 EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(0, &start, &length));
101 EXPECT_EQ(0, start);
102 EXPECT_EQ(11, length);
103 EXPECT_EQ(UBIDI_LTR, iterator()->GetVisualRun(1, &start, &length));
104 EXPECT_EQ(11, start);
105 EXPECT_EQ(9, length);
106 EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(2, &start, &length));
107 EXPECT_EQ(20, start);
108 EXPECT_EQ(19, length);
109
110 int end;
111 UBiDiLevel level;
112 iterator()->GetLogicalRun(0, &end, &level);
113 EXPECT_EQ(11, end);
114 EXPECT_EQ(1, level);
115 iterator()->GetLogicalRun(11, &end, &level);
116 EXPECT_EQ(20, end);
117 EXPECT_EQ(0, level);
118 iterator()->GetLogicalRun(20, &end, &level);
119 EXPECT_EQ(39, end);
120 EXPECT_EQ(1, level);
121 }
122 }
123
TEST_P(BiDiLineIteratorTest,RTLPunctuationNoCustomBehavior)124 TEST_P(BiDiLineIteratorTest, RTLPunctuationNoCustomBehavior) {
125 // This string features Hebrew characters interleaved with ASCII punctuation.
126 iterator()->Open(UTF8ToUTF16("א!ב\"ג#ד$ה%ו&ז'ח(ט)י*ך+כ,ל-ם.מ/"
127 "ן:נ;ס<ע=ף>פ?ץ@צ[ק\\ר]ש^ת_א`ב{ג|ד}ה~ו"),
128 GetParam(), BiDiLineIterator::CustomBehavior::NONE);
129
130 // Expect a single RTL run.
131 ASSERT_EQ(1, iterator()->CountRuns());
132
133 int start, length;
134 EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(0, &start, &length));
135 EXPECT_EQ(0, start);
136 EXPECT_EQ(65, length);
137
138 int end;
139 UBiDiLevel level;
140 iterator()->GetLogicalRun(0, &end, &level);
141 EXPECT_EQ(65, end);
142 EXPECT_EQ(1, level);
143 }
144
TEST_P(BiDiLineIteratorTest,RTLPunctuationAsURL)145 TEST_P(BiDiLineIteratorTest, RTLPunctuationAsURL) {
146 // This string features Hebrew characters interleaved with ASCII punctuation.
147 iterator()->Open(UTF8ToUTF16("א!ב\"ג#ד$ה%ו&ז'ח(ט)י*ך+כ,ל-ם.מ/"
148 "ן:נ;ס<ע=ף>פ?ץ@צ[ק\\ר]ש^ת_א`ב{ג|ד}ה~ו"),
149 GetParam(), BiDiLineIterator::CustomBehavior::AS_URL);
150
151 const int kStringSize = 65;
152
153 // Expect a primary RTL run, broken up by each of the 8 punctuation marks that
154 // are considered strong LTR (17 runs total).
155 struct {
156 int start;
157 UBiDiDirection dir;
158 } expected_runs[] = {
159 {0, UBIDI_RTL}, {5, UBIDI_LTR}, // '#'
160 {6, UBIDI_RTL}, {11, UBIDI_LTR}, // '&'
161 {12, UBIDI_RTL}, {27, UBIDI_LTR}, // '.'
162 {28, UBIDI_RTL}, {29, UBIDI_LTR}, // '/'
163 {30, UBIDI_RTL}, {31, UBIDI_LTR}, // ':'
164 {32, UBIDI_RTL}, {37, UBIDI_LTR}, // '='
165 {38, UBIDI_RTL}, {41, UBIDI_LTR}, // '?'
166 {42, UBIDI_RTL}, {43, UBIDI_LTR}, // '@'
167 {44, UBIDI_RTL},
168 };
169
170 ASSERT_EQ(arraysize(expected_runs),
171 static_cast<size_t>(iterator()->CountRuns()));
172
173 for (size_t i = 0; i < arraysize(expected_runs); ++i) {
174 const auto& expected_run = expected_runs[i];
175 int expected_run_end = i >= arraysize(expected_runs) - 1
176 ? kStringSize
177 : expected_runs[i + 1].start;
178
179 size_t visual_index = GetParam() == TextDirection::RIGHT_TO_LEFT
180 ? arraysize(expected_runs) - 1 - i
181 : i;
182 int start, length;
183 EXPECT_EQ(expected_run.dir,
184 iterator()->GetVisualRun(visual_index, &start, &length))
185 << "(i = " << i << ")";
186 EXPECT_EQ(expected_run.start, start) << "(i = " << i << ")";
187 EXPECT_EQ(expected_run_end - expected_run.start, length)
188 << "(i = " << i << ")";
189
190 int expected_level =
191 expected_run.dir == UBIDI_RTL
192 ? 1
193 : (GetParam() == TextDirection::RIGHT_TO_LEFT ? 2 : 0);
194 int end;
195 UBiDiLevel level;
196 iterator()->GetLogicalRun(expected_run.start, &end, &level);
197 EXPECT_EQ(expected_run_end, end) << "(i = " << i << ")";
198 EXPECT_EQ(expected_level, level) << "(i = " << i << ")";
199 }
200 }
201
202 INSTANTIATE_TEST_CASE_P(,
203 BiDiLineIteratorTest,
204 ::testing::Values(TextDirection::LEFT_TO_RIGHT,
205 TextDirection::RIGHT_TO_LEFT));
206
207 } // namespace
208 } // namespace i18n
209 } // namespace base
210