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