• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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/message_center/views/bounded_label.h"
6 
7 #include <limits>
8 
9 #include "base/strings/string_split.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "ui/gfx/font_list.h"
14 #include "ui/gfx/text_utils.h"
15 #include "ui/views/controls/label.h"
16 
17 namespace message_center {
18 
19 namespace test {
20 
21 /* Test fixture ***************************************************************/
22 
23 class BoundedLabelTest : public testing::Test {
24  public:
BoundedLabelTest()25   BoundedLabelTest() {
26     digit_pixels_ = gfx::GetStringWidth(base::UTF8ToUTF16("0"), font_list_);
27     space_pixels_ = gfx::GetStringWidth(base::UTF8ToUTF16(" "), font_list_);
28     ellipsis_pixels_ = gfx::GetStringWidth(base::UTF8ToUTF16("\xE2\x80\xA6"),
29                                            font_list_);
30   }
31 
~BoundedLabelTest()32   virtual ~BoundedLabelTest() {}
33 
34   // Replaces all occurences of three periods ("...") in the specified string
35   // with an ellipses character (UTF8 "\xE2\x80\xA6") and returns a string16
36   // with the results. This allows test strings to be specified as ASCII const
37   // char* strings, making tests more readable and easier to write.
ToString(const char * string)38   base::string16 ToString(const char* string) {
39     const base::string16 periods = base::UTF8ToUTF16("...");
40     const base::string16 ellipses = base::UTF8ToUTF16("\xE2\x80\xA6");
41     base::string16 result = base::UTF8ToUTF16(string);
42     ReplaceSubstringsAfterOffset(&result, 0, periods, ellipses);
43     return result;
44   }
45 
46   // Converts the specified elision width to pixels. To make tests somewhat
47   // independent of the fonts of the platform on which they're run, the elision
48   // widths are specified as XYZ integers, with the corresponding width in
49   // pixels being X times the width of digit characters plus Y times the width
50   // of spaces plus Z times the width of ellipses in the default font of the
51   // test plaform. It is assumed that all digits have the same width in that
52   // font, that this width is greater than the width of spaces, and that the
53   // width of 3 digits is greater than the width of ellipses.
ToPixels(int width)54   int ToPixels(int width) {
55     return digit_pixels_ * width / 100 +
56            space_pixels_ * (width % 100) / 10 +
57            ellipsis_pixels_ * (width % 10);
58   }
59 
60   // Exercise BounderLabel::GetWrappedText() using the fixture's test label.
GetWrappedText(int width)61   base::string16 GetWrappedText(int width) {
62     return label_->GetWrappedTextForTest(width, lines_);
63   }
64 
65   // Exercise BounderLabel::GetLinesForWidthAndLimit() using the test label.
GetLinesForWidth(int width)66   int GetLinesForWidth(int width) {
67     label_->SetBounds(0, 0, width, font_list_.GetHeight() * lines_);
68     return label_->GetLinesForWidthAndLimit(width, lines_);
69   }
70 
71  protected:
72   // Creates a label to test with. Returns this fixture, which can be used to
73   // test the newly created label using the exercise methods above.
Label(base::string16 text,int lines)74   BoundedLabelTest& Label(base::string16 text, int lines) {
75     lines_ = lines;
76     label_.reset(new BoundedLabel(text, font_list_));
77     label_->SetLineLimit(lines_);
78     return *this;
79   }
80 
81  private:
82   // The default font list, which will be used for tests.
83   gfx::FontList font_list_;
84   int digit_pixels_;
85   int space_pixels_;
86   int ellipsis_pixels_;
87   scoped_ptr<BoundedLabel> label_;
88   int lines_;
89 };
90 
91 /* Test macro *****************************************************************/
92 
93 #define TEST_WRAP(expected, text, width, lines) \
94   EXPECT_EQ(ToString(expected), \
95             Label(ToString(text), lines).GetWrappedText(ToPixels(width)))
96 
97 #define TEST_LINES(expected, text, width, lines) \
98   EXPECT_EQ(expected, \
99             Label(ToString(text), lines).GetLinesForWidth(ToPixels(width)))
100 
101 /* Elision tests **************************************************************/
102 
TEST_F(BoundedLabelTest,GetWrappedTextTest)103 TEST_F(BoundedLabelTest, GetWrappedTextTest) {
104   // One word per line: No ellision should be made when not necessary.
105   TEST_WRAP("123", "123", 301, 1);
106   TEST_WRAP("123", "123", 301, 2);
107   TEST_WRAP("123", "123", 301, 3);
108   TEST_WRAP("123\n456", "123 456", 301, 2);
109   TEST_WRAP("123\n456", "123 456", 301, 3);
110   TEST_WRAP("123\n456\n789", "123 456 789", 301, 3);
111 
112   // One word per line: Ellisions should be made when necessary.
113   TEST_WRAP("123...", "123 456", 301, 1);
114   TEST_WRAP("123...", "123 456 789", 301, 1);
115   TEST_WRAP("123\n456...", "123 456 789", 301, 2);
116 
117   // Two words per line: No ellision should be made when not necessary.
118   TEST_WRAP("123 456", "123 456", 621, 1);
119   TEST_WRAP("123 456", "123 456", 621, 2);
120   TEST_WRAP("123 456", "123 456", 621, 3);
121   TEST_WRAP("123 456\n789 012", "123 456 789 012", 621, 2);
122   TEST_WRAP("123 456\n789 012", "123 456 789 012", 621, 3);
123   TEST_WRAP("123 456\n789 012\n345 678", "123 456 789 012 345 678", 621, 3);
124 
125   // Two words per line: Ellisions should be made when necessary.
126   TEST_WRAP("123 456...", "123 456 789 012", 621, 1);
127   TEST_WRAP("123 456...", "123 456 789 012 345 678", 621, 1);
128   TEST_WRAP("123 456\n789 012...", "123 456 789 012 345 678", 621, 2);
129 
130   // Single trailing spaces: No ellipses should be added.
131   TEST_WRAP("123", "123 ", 301, 1);
132   TEST_WRAP("123\n456", "123 456 ", 301, 2);
133   TEST_WRAP("123\n456\n789", "123 456 789 ", 301, 3);
134   TEST_WRAP("123 456", "123 456 ", 611, 1);
135   TEST_WRAP("123 456\n789 012", "123 456 789 012 ", 611, 2);
136   TEST_WRAP("123 456\n789 012\n345 678", "123 456 789 012 345 678 ", 611, 3);
137 
138   // Multiple trailing spaces: No ellipses should be added.
139   TEST_WRAP("123", "123         ", 301, 1);
140   TEST_WRAP("123\n456", "123 456         ", 301, 2);
141   TEST_WRAP("123\n456\n789", "123 456 789         ", 301, 3);
142   TEST_WRAP("123 456", "123 456         ", 611, 1);
143   TEST_WRAP("123 456\n789 012", "123 456 789 012         ", 611, 2);
144   TEST_WRAP("123 456\n789 012\n345 678",
145             "123 456 789 012 345 678         ", 611, 3);
146 
147   // Multiple spaces between words on the same line: Spaces should be preserved.
148   // Test cases for single spaces between such words are included in the "Two
149   // words per line" sections above.
150   TEST_WRAP("123  456", "123  456", 621, 1);
151   TEST_WRAP("123  456...", "123  456 789   012", 631, 1);
152   TEST_WRAP("123  456\n789   012", "123  456 789   012", 631, 2);
153   TEST_WRAP("123  456...", "123  456 789   012  345    678", 621, 1);
154   TEST_WRAP("123  456\n789   012...", "123  456 789   012 345    678", 631, 2);
155   TEST_WRAP("123  456\n789   012\n345    678",
156             "123  456 789   012 345    678", 641, 3);
157 
158   // Multiple spaces between words split across lines: Spaces should be removed
159   // even if lines are wide enough to include those spaces. Test cases for
160   // single spaces between such words are included in the "Two words  per line"
161   // sections above.
162   TEST_WRAP("123\n456", "123  456", 321, 2);
163   TEST_WRAP("123\n456", "123         456", 391, 2);
164   TEST_WRAP("123\n456...", "123  456  789", 321, 2);
165   TEST_WRAP("123\n456...", "123         456         789", 391, 2);
166   TEST_WRAP("123  456\n789  012", "123  456  789  012", 641, 2);
167   TEST_WRAP("123  456\n789  012...", "123  456  789  012  345  678", 641, 2);
168 
169   // Long words without spaces should be wrapped when necessary.
170   TEST_WRAP("123\n456", "123456", 300, 9);
171 
172   // TODO(dharcourt): Add test cases to verify that:
173   // - Spaces before elisions are removed
174   // - Leading spaces are preserved
175   // - Words are split when they are longer than lines
176   // - Words are clipped when they are longer than the last line
177   // - No blank line are created before or after clipped word
178   // - Spaces at the end of the text are removed
179 
180   // TODO(dharcourt): Add test cases for:
181   // - Empty and very large strings
182   // - Zero, very large, and negative line limit values
183   // - Other input boundary conditions
184   // TODO(dharcourt): Add some randomly generated fuzz test cases.
185 
186 }
187 
188 /* GetLinesTest ***************************************************************/
189 
TEST_F(BoundedLabelTest,GetLinesTest)190 TEST_F(BoundedLabelTest, GetLinesTest) {
191   // Zero and negative width values should yield zero lines.
192   TEST_LINES(0, "123 456", 0, 1);
193   TEST_LINES(0, "123 456", -1, 1);
194   TEST_LINES(0, "123 456", -2, 1);
195   TEST_LINES(0, "123 456", std::numeric_limits<int>::min(), 1);
196 
197   // Small width values should yield one word per line.
198   TEST_LINES(1, "123 456", 1, 1);
199   TEST_LINES(2, "123 456", 1, 2);
200   TEST_LINES(1, "123 456", 2, 1);
201   TEST_LINES(2, "123 456", 2, 2);
202   TEST_LINES(1, "123 456", 3, 1);
203   TEST_LINES(2, "123 456", 3, 2);
204 
205   // Large width values should yield all words on one line.
206   TEST_LINES(1, "123 456", 610, 1);
207   TEST_LINES(1, "123 456", std::numeric_limits<int>::max(), 1);
208 }
209 
210 /* Other tests ****************************************************************/
211 
212 // TODO(dharcourt): Add test cases to verify that:
213 // - SetMaxLines() affects the return values of some methods but not others.
214 // - Bound changes affects GetPreferredLines(), GetTextSize(), and
215 //   GetWrappedText() return values.
216 // - GetTextFlags are as expected.
217 
218 }  // namespace test
219 
220 }  // namespace message_center
221