1 // Copyright (c) 2011 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 "ui/gfx/text_utils.h"
6 
7 #include <stddef.h>
8 
9 #include "base/stl_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "build/build_config.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "ui/gfx/font_list.h"
14 
15 namespace gfx {
16 namespace {
17 
18 const base::char16 kAcceleratorChar = '&';
19 
20 struct RemoveAcceleratorCharData {
21   const char* input;
22   int accelerated_char_pos;
23   int accelerated_char_span;
24   const char* output;
25   const char* name;
26 };
27 
TEST(TextUtilsTest,GetStringWidth)28 TEST(TextUtilsTest, GetStringWidth) {
29   FontList font_list;
30   EXPECT_EQ(GetStringWidth(base::string16(), font_list), 0);
31   EXPECT_GT(GetStringWidth(base::ASCIIToUTF16("a"), font_list),
32             GetStringWidth(base::string16(), font_list));
33   EXPECT_GT(GetStringWidth(base::ASCIIToUTF16("ab"), font_list),
34             GetStringWidth(base::ASCIIToUTF16("a"), font_list));
35   EXPECT_GT(GetStringWidth(base::ASCIIToUTF16("abc"), font_list),
36             GetStringWidth(base::ASCIIToUTF16("ab"), font_list));
37 }
38 
39 class RemoveAcceleratorCharTest
40     : public testing::TestWithParam<RemoveAcceleratorCharData> {
41  public:
42   static const RemoveAcceleratorCharData kCases[];
43 };
44 
45 const RemoveAcceleratorCharData RemoveAcceleratorCharTest::kCases[] = {
46     {"", -1, 0, "", "EmptyString"},
47     {"&", -1, 0, "", "AcceleratorCharOnly"},
48     {"no accelerator", -1, 0, "no accelerator", "NoAccelerator"},
49     {"&one accelerator", 0, 1, "one accelerator", "OneAccelerator_Start"},
50     {"one &accelerator", 4, 1, "one accelerator", "OneAccelerator_Middle"},
51     {"one_accelerator&", -1, 0, "one_accelerator", "OneAccelerator_End"},
52     {"&two &accelerators", 4, 1, "two accelerators",
53      "TwoAccelerators_OneAtStart"},
54     {"two &accelerators&", 4, 1, "two accelerators",
55      "TwoAccelerators_OneAtEnd"},
56     {"two& &accelerators", 4, 1, "two accelerators",
57      "TwoAccelerators_SpaceBetween"},
58     {"&&escaping", -1, 0, "&escaping", "Escape_Start"},
59     {"escap&&ing", -1, 0, "escap&ing", "Escape_Middle"},
60     {"escaping&&", -1, 0, "escaping&", "Escape_End"},
61     {"&mix&&ed", 0, 1, "mix&ed", "Mixed_EscapeAfterAccelerator"},
62     {"&&m&ix&&e&d&", 6, 1, "&mix&ed", "Mixed_MiddleAcceleratorSkipped"},
63     {"&&m&&ix&ed&&", 5, 1, "&m&ixed&", "Mixed_OneAccelerator"},
64     {"&m&&ix&ed&&", 4, 1, "m&ixed&", "Mixed_InitialAcceleratorSkipped"},
65     // U+1D49C MATHEMATICAL SCRIPT CAPITAL A, which occupies two |char16|'s.
66     {"&\U0001D49C", 0, 2, "\U0001D49C", "MultibyteAccelerator_Start"},
67     {"Test&\U0001D49Cing", 4, 2, "Test\U0001D49Cing",
68      "MultibyteAccelerator_Middle"},
69     {"Test\U0001D49C&ing", 6, 1, "Test\U0001D49Cing",
70      "OneAccelerator_AfterMultibyte"},
71     {"Test&\U0001D49C&ing", 6, 1, "Test\U0001D49Cing",
72      "MultibyteAccelerator_Skipped"},
73     {"Test&\U0001D49C&&ing", 4, 2, "Test\U0001D49C&ing",
74      "MultibyteAccelerator_EscapeAfter"},
75     {"Test&\U0001D49C&\U0001D49Cing", 6, 2, "Test\U0001D49C\U0001D49Cing",
76      "MultibyteAccelerator_AfterMultibyteAccelerator"},
77 };
78 
79 INSTANTIATE_TEST_SUITE_P(
80     All,
81     RemoveAcceleratorCharTest,
82     testing::ValuesIn(RemoveAcceleratorCharTest::kCases),
__anon5c47ba490202(const testing::TestParamInfo<RemoveAcceleratorCharData>& param_info) 83     [](const testing::TestParamInfo<RemoveAcceleratorCharData>& param_info) {
84       return param_info.param.name;
85     });
86 
TEST_P(RemoveAcceleratorCharTest,RemoveAcceleratorChar)87 TEST_P(RemoveAcceleratorCharTest, RemoveAcceleratorChar) {
88   RemoveAcceleratorCharData data = GetParam();
89   int accelerated_char_pos;
90   int accelerated_char_span;
91   base::string16 result =
92       RemoveAcceleratorChar(base::UTF8ToUTF16(data.input), kAcceleratorChar,
93                             &accelerated_char_pos, &accelerated_char_span);
94   EXPECT_EQ(result, base::UTF8ToUTF16(data.output));
95   EXPECT_EQ(accelerated_char_pos, data.accelerated_char_pos);
96   EXPECT_EQ(accelerated_char_span, data.accelerated_char_span);
97 }
98 
99 struct FindValidBoundaryData {
100   const char16_t* input;
101   size_t index_in;
102   bool trim_whitespace;
103   size_t index_out;
104   const char* name;
105 };
106 
107 class FindValidBoundaryBeforeTest
108     : public testing::TestWithParam<FindValidBoundaryData> {
109  public:
110   static const FindValidBoundaryData kCases[];
111 };
112 
113 const FindValidBoundaryData FindValidBoundaryBeforeTest::kCases[] = {
114     {u"", 0, false, 0, "Empty"},
115     {u"word", 0, false, 0, "StartOfString"},
116     {u"word", 4, false, 4, "EndOfString"},
117     {u"word", 2, false, 2, "MiddleOfString_OnValidCharacter"},
118     {u"w��d", 2, false, 1, "MiddleOfString_OnSurrogatePair"},
119     {u"w��d", 1, false, 1, "MiddleOfString_BeforeSurrogatePair"},
120     {u"w��d", 3, false, 3, "MiddleOfString_AfterSurrogatePair"},
121     {u"wo d", 3, false, 3, "MiddleOfString_OnSpace_NoTrim"},
122     {u"wo d", 3, true, 2, "MiddleOfString_OnSpace_Trim"},
123     {u"wo d", 2, false, 2, "MiddleOfString_LeftOfSpace_NoTrim"},
124     {u"wo d", 2, true, 2, "MiddleOfString_LeftOfSpace_Trim"},
125     {u"wo\td", 3, false, 3, "MiddleOfString_OnTab_NoTrim"},
126     {u"wo\td", 3, true, 2, "MiddleOfString_OnTab_Trim"},
127     {u"w  d", 3, false, 3, "MiddleOfString_MultipleWhitespace_NoTrim"},
128     {u"w  d", 3, true, 1, "MiddleOfString_MultipleWhitespace_Trim"},
129     {u"w  d", 2, false, 2, "MiddleOfString_MiddleOfWhitespace_NoTrim"},
130     {u"w  d", 2, true, 1, "MiddleOfString_MiddleOfWhitespace_Trim"},
131     {u"w  ", 3, false, 3, "EndOfString_Whitespace_NoTrim"},
132     {u"w  ", 3, true, 1, "EndOfString_Whitespace_Trim"},
133     {u"  d", 2, false, 2, "MiddleOfString_Whitespace_NoTrim"},
134     {u"  d", 2, true, 0, "MiddleOfString_Whitespace_Trim"},
135     // COMBINING GRAVE ACCENT (U+0300)
136     {u"wo\u0300d", 2, false, 1, "MiddleOfString_OnCombiningMark"},
137     {u"wo\u0300d", 1, false, 1, "MiddleOfString_BeforeCombiningMark"},
138     {u"wo\u0300d", 3, false, 3, "MiddleOfString_AfterCombiningMark"},
139     {u"w o\u0300d", 3, true, 1, "MiddleOfString_SpaceAndCombinginMark_Trim"},
140     {u"wo\u0300 d", 3, true, 3, "MiddleOfString_CombiningMarkAndSpace_Trim"},
141     {u"w \u0300d", 3, true, 3,
142      "MiddleOfString_AfterSpaceWithCombiningMark_Trim"},
143     {u"w \u0300d", 2, true, 1, "MiddleOfString_OnSpaceWithCombiningMark_Trim"},
144     {u"w \u0300 d", 4, true, 3,
145      "MiddleOfString_AfterSpaceAfterSpaceWithCombiningMark_Trim"},
146     // MUSICAL SYMBOL G CLEF (U+1D11E) + MUSICAL SYMBOL COMBINING FLAG-1
147     // (U+1D16E)
148     {u"w\U0001D11E\U0001D16Ed", 1, false, 1,
149      "MiddleOfString_BeforeCombiningSurrogate"},
150     {u"w\U0001D11E\U0001D16Ed", 2, false, 1,
151      "MiddleOfString_OnCombiningSurrogate_Pos1"},
152     {u"w\U0001D11E\U0001D16Ed", 3, false, 1,
153      "MiddleOfString_OnCombiningSurrogate_Pos2"},
154     {u"w\U0001D11E\U0001D16Ed", 4, false, 1,
155      "MiddleOfString_OnCombiningSurrogate_Pos3"},
156     {u"w\U0001D11E\U0001D16Ed", 5, false, 5,
157      "MiddleOfString_AfterCombiningSurrogate"},
158 };
159 
160 INSTANTIATE_TEST_SUITE_P(
161     All,
162     FindValidBoundaryBeforeTest,
163     testing::ValuesIn(FindValidBoundaryBeforeTest::kCases),
__anon5c47ba490302(const testing::TestParamInfo<FindValidBoundaryData>& param_info) 164     [](const testing::TestParamInfo<FindValidBoundaryData>& param_info) {
165       return param_info.param.name;
166     });
167 
TEST_P(FindValidBoundaryBeforeTest,FindValidBoundaryBefore)168 TEST_P(FindValidBoundaryBeforeTest, FindValidBoundaryBefore) {
169   FindValidBoundaryData data = GetParam();
170   const base::string16::const_pointer input =
171       reinterpret_cast<base::string16::const_pointer>(data.input);
172   DLOG(INFO) << input;
173   size_t result =
174       FindValidBoundaryBefore(input, data.index_in, data.trim_whitespace);
175   EXPECT_EQ(data.index_out, result);
176 }
177 
178 class FindValidBoundaryAfterTest
179     : public testing::TestWithParam<FindValidBoundaryData> {
180  public:
181   static const FindValidBoundaryData kCases[];
182 };
183 
184 const FindValidBoundaryData FindValidBoundaryAfterTest::kCases[] = {
185     {u"", 0, false, 0, "Empty"},
186     {u"word", 0, false, 0, "StartOfString"},
187     {u"word", 4, false, 4, "EndOfString"},
188     {u"word", 2, false, 2, "MiddleOfString_OnValidCharacter"},
189     {u"w��d", 2, false, 3, "MiddleOfString_OnSurrogatePair"},
190     {u"w��d", 1, false, 1, "MiddleOfString_BeforeSurrogatePair"},
191     {u"w��d", 3, false, 3, "MiddleOfString_AfterSurrogatePair"},
192     {u"wo d", 2, false, 2, "MiddleOfString_OnSpace_NoTrim"},
193     {u"wo d", 2, true, 3, "MiddleOfString_OnSpace_Trim"},
194     {u"wo d", 3, false, 3, "MiddleOfString_RightOfSpace_NoTrim"},
195     {u"wo d", 3, true, 3, "MiddleOfString_RightOfSpace_Trim"},
196     {u"wo\td", 2, false, 2, "MiddleOfString_OnTab_NoTrim"},
197     {u"wo\td", 2, true, 3, "MiddleOfString_OnTab_Trim"},
198     {u"w  d", 1, false, 1, "MiddleOfString_MultipleWhitespace_NoTrim"},
199     {u"w  d", 1, true, 3, "MiddleOfString_MultipleWhitespace_Trim"},
200     {u"w  d", 2, false, 2, "MiddleOfString_MiddleOfWhitespace_NoTrim"},
201     {u"w  d", 2, true, 3, "MiddleOfString_MiddleOfWhitespace_Trim"},
202     {u"w  ", 1, false, 1, "MiddleOfString_Whitespace_NoTrim"},
203     {u"w  ", 1, true, 3, "MiddleOfString_Whitespace_Trim"},
204     {u"  d", 0, false, 0, "StartOfString_Whitespace_NoTrim"},
205     {u"  d", 0, true, 2, "StartOfString_Whitespace_Trim"},
206     // COMBINING GRAVE ACCENT (U+0300)
207     {u"wo\u0300d", 2, false, 3, "MiddleOfString_OnCombiningMark"},
208     {u"wo\u0300d", 1, false, 1, "MiddleOfString_BeforeCombiningMark"},
209     {u"wo\u0300d", 3, false, 3, "MiddleOfString_AfterCombiningMark"},
210     {u"w o\u0300d", 1, true, 2, "MiddleOfString_SpaceAndCombinginMark_Trim"},
211     {u"wo\u0300 d", 1, true, 1,
212      "MiddleOfString_BeforeCombiningMarkAndSpace_Trim"},
213     {u"wo\u0300 d", 2, true, 4, "MiddleOfString_OnCombiningMarkAndSpace_Trim"},
214     {u"w \u0300d", 1, true, 1,
215      "MiddleOfString_BeforeSpaceWithCombiningMark_Trim"},
216     {u"w \u0300d", 2, true, 3, "MiddleOfString_OnSpaceWithCombiningMark_Trim"},
217     {u"w  \u0300d", 1, true, 2,
218      "MiddleOfString_BeforeSpaceBeforeSpaceWithCombiningMark_Trim"},
219     // MUSICAL SYMBOL G CLEF (U+1D11E) + MUSICAL SYMBOL COMBINING FLAG-1
220     // (U+1D16E)
221     {u"w\U0001D11E\U0001D16Ed", 1, false, 1,
222      "MiddleOfString_BeforeCombiningSurrogate"},
223     {u"w\U0001D11E\U0001D16Ed", 2, false, 5,
224      "MiddleOfString_OnCombiningSurrogate_Pos1"},
225     {u"w\U0001D11E\U0001D16Ed", 3, false, 5,
226      "MiddleOfString_OnCombiningSurrogate_Pos2"},
227     {u"w\U0001D11E\U0001D16Ed", 4, false, 5,
228      "MiddleOfString_OnCombiningSurrogate_Pos3"},
229     {u"w\U0001D11E\U0001D16Ed", 5, false, 5,
230      "MiddleOfString_AfterCombiningSurrogate"},
231 };
232 
233 INSTANTIATE_TEST_SUITE_P(
234     All,
235     FindValidBoundaryAfterTest,
236     testing::ValuesIn(FindValidBoundaryAfterTest::kCases),
__anon5c47ba490402(const testing::TestParamInfo<FindValidBoundaryData>& param_info) 237     [](const testing::TestParamInfo<FindValidBoundaryData>& param_info) {
238       return param_info.param.name;
239     });
240 
TEST_P(FindValidBoundaryAfterTest,FindValidBoundaryAfter)241 TEST_P(FindValidBoundaryAfterTest, FindValidBoundaryAfter) {
242   FindValidBoundaryData data = GetParam();
243   const base::string16::const_pointer input =
244       reinterpret_cast<base::string16::const_pointer>(data.input);
245   size_t result =
246       FindValidBoundaryAfter(input, data.index_in, data.trim_whitespace);
247   EXPECT_EQ(data.index_out, result);
248 }
249 
250 }  // namespace
251 }  // namespace gfx
252