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: 25 BoundedLabelTest() { 26 digit_pixels_ = gfx::GetStringWidth(UTF8ToUTF16("0"), font_list_); 27 space_pixels_ = gfx::GetStringWidth(UTF8ToUTF16(" "), font_list_); 28 ellipsis_pixels_ = gfx::GetStringWidth(UTF8ToUTF16("\xE2\x80\xA6"), 29 font_list_); 30 } 31 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. 38 string16 ToString(const char* string) { 39 const string16 periods = UTF8ToUTF16("..."); 40 const string16 ellipses = UTF8ToUTF16("\xE2\x80\xA6"); 41 string16 result = 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. 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. 61 string16 GetWrappedText(int width) { 62 return label_->GetWrappedTextForTest(width, lines_); 63 } 64 65 // Exercise BounderLabel::GetLinesForWidthAndLimit() using the test label. 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. 74 BoundedLabelTest& Label(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 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 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