OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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 "chrome/browser/ui/views/tabs/touch_tab_strip_layout.h" |
| 6 |
| 7 #include <string> |
| 8 |
| 9 #include "base/string_number_conversions.h" |
| 10 #include "base/string_util.h" |
| 11 #include "testing/gtest/include/gtest/gtest.h" |
| 12 #include "ui/views/view.h" |
| 13 #include "ui/views/view_model.h" |
| 14 |
| 15 namespace { |
| 16 |
| 17 struct CommonTestData { |
| 18 const int initial_x; |
| 19 const int width; |
| 20 const int tab_size; |
| 21 const int tab_padding; |
| 22 const int stacked_offset; |
| 23 const int mini_tab_count; |
| 24 const int active_index; |
| 25 const std::string start_bounds; |
| 26 const std::string expected_bounds; |
| 27 }; |
| 28 |
| 29 } // namespace |
| 30 |
| 31 class TouchTabStripLayoutTest : public testing::Test { |
| 32 public: |
| 33 TouchTabStripLayoutTest() {} |
| 34 |
| 35 protected: |
| 36 void Reset(TouchTabStripLayout* layout, |
| 37 int x, |
| 38 int width, |
| 39 int mini_tab_count, |
| 40 int active_index) { |
| 41 layout->Reset(x, width, mini_tab_count, active_index); |
| 42 } |
| 43 |
| 44 void CreateLayout(const CommonTestData& data) { |
| 45 if (!data.start_bounds.empty()) |
| 46 PrepareChildViewsFromString(data.start_bounds); |
| 47 else |
| 48 PrepareChildViewsFromString(data.expected_bounds); |
| 49 layout_.reset(new TouchTabStripLayout( |
| 50 gfx::Size(data.tab_size, 10), data.tab_padding, |
| 51 data.stacked_offset, 4, &view_model_)); |
| 52 if (data.start_bounds.empty()) { |
| 53 PrepareChildViewsFromString(data.expected_bounds); |
| 54 layout_->Reset(data.initial_x, data.width, data.mini_tab_count, |
| 55 data.active_index); |
| 56 } else { |
| 57 ASSERT_NO_FATAL_FAILURE(SetBoundsFromString(data.start_bounds)); |
| 58 layout_->Reset(data.initial_x, data.width, data.mini_tab_count, |
| 59 data.active_index); |
| 60 ASSERT_NO_FATAL_FAILURE(SetBoundsFromString(data.start_bounds)); |
| 61 } |
| 62 } |
| 63 |
| 64 void AddViewToViewModel(int index) { |
| 65 views::View* child_view = new views::View; |
| 66 view_.AddChildView(child_view); |
| 67 view_model_.Add(child_view, index); |
| 68 } |
| 69 |
| 70 void PrepareChildViewsFromString(const std::string& bounds) { |
| 71 std::vector<std::string> positions; |
| 72 Tokenize(bounds, " ", &positions); |
| 73 PrepareChildViews(static_cast<int>(positions.size())); |
| 74 } |
| 75 |
| 76 void PrepareChildViews(int count) { |
| 77 view_model_.Clear(); |
| 78 view_.RemoveAllChildViews(true); |
| 79 for (int i = 0; i < count; ++i) |
| 80 AddViewToViewModel(i); |
| 81 } |
| 82 |
| 83 void SetBoundsFromString(const std::string& bounds) { |
| 84 std::vector<std::string> positions; |
| 85 Tokenize(bounds, " ", &positions); |
| 86 PrepareChildViews(static_cast<int>(positions.size())); |
| 87 for (int i = 0; i < view_model_.view_size(); ++i) { |
| 88 int x = 0; |
| 89 gfx::Rect bounds(view_model_.ideal_bounds(i)); |
| 90 ASSERT_TRUE(base::StringToInt(positions[i], &x)); |
| 91 bounds.set_x(x); |
| 92 view_model_.set_ideal_bounds(i, bounds); |
| 93 } |
| 94 } |
| 95 |
| 96 std::string BoundsString() const { |
| 97 std::string result; |
| 98 for (int i = 0; i < view_model_.view_size(); ++i) { |
| 99 if (!result.empty()) |
| 100 result += " "; |
| 101 result += base::IntToString(view_model_.ideal_bounds(i).x()); |
| 102 } |
| 103 return result; |
| 104 } |
| 105 |
| 106 std::string BoundsString2(int active_index) const { |
| 107 std::string result; |
| 108 for (int i = 0; i < view_model_.view_size(); ++i) { |
| 109 if (!result.empty()) |
| 110 result += " "; |
| 111 if (i == active_index) |
| 112 result += "["; |
| 113 result += base::IntToString(view_model_.ideal_bounds(i).x()); |
| 114 if (i == active_index) |
| 115 result += "]"; |
| 116 } |
| 117 return result; |
| 118 } |
| 119 |
| 120 void Validate(int active_index, int max_width) { |
| 121 // Make sure none of the tabs are more than 90 apart |
| 122 // (tab_size(100) + padding (-10)). |
| 123 for (int j = 1; j < view_model_.view_size(); ++j) |
| 124 EXPECT_LE(ideal_x(j) - ideal_x(j - 1), max_width - 100); |
| 125 } |
| 126 |
| 127 int ideal_x(int index) const { |
| 128 return view_model_.ideal_bounds(index).x(); |
| 129 } |
| 130 |
| 131 scoped_ptr<TouchTabStripLayout> layout_; |
| 132 views::ViewModel view_model_; |
| 133 |
| 134 private: |
| 135 views::View view_; |
| 136 |
| 137 DISALLOW_COPY_AND_ASSIGN(TouchTabStripLayoutTest); |
| 138 }; |
| 139 |
| 140 // Random data. |
| 141 TEST_F(TouchTabStripLayoutTest, ValidateInitialLayout) { |
| 142 TouchTabStripLayout layout(gfx::Size(100, 10), -10, 2, 4, &view_model_); |
| 143 PrepareChildViews(12); |
| 144 |
| 145 for (int i = 120; i < 600; ++i) { |
| 146 for (int j = 0; j < 12; ++j) { |
| 147 Reset(&layout, 0, i, 0, j); |
| 148 Validate(j, i); |
| 149 if (HasNonfatalFailure()) |
| 150 return; |
| 151 } |
| 152 } |
| 153 } |
| 154 |
| 155 // Ensure initial layout is correct. |
| 156 TEST_F(TouchTabStripLayoutTest, InitialLayout) { |
| 157 struct CommonTestData test_data[] = { |
| 158 { 0, 198, 100, -10, 1, 0, 9, "", |
| 159 "0 0 0 0 0 0 1 2 3 4 94 95 96 97 98 98 98 98" }, |
| 160 { 0, 198, 100, -10, 1, 0, 0, "", "0 90 94 95 96 97 98 98 98" }, |
| 161 { 0, 300, 100, -10, 1, 0, 0, "", |
| 162 "0 90 180 196 197 198 199 200 200 200 200" }, |
| 163 { 0, 300, 100, -10, 1, 0, 10, "", "0 0 0 0 1 2 3 4 20 110 200" }, |
| 164 { 0, 300, 100, -10, 1, 0, 1, "", "0 90 180 196 197 198 199 200 200" }, |
| 165 { 0, 643, 160, -27, 6, 0, 0, "", "0 133 266 399" }, |
| 166 { 0, 300, 100, -10, 1, 0, 7, "", "0 1 2 3 4 20 110 200" }, |
| 167 { 0, 300, 100, -10, 1, 0, 6, "", "0 1 2 3 4 20 110 200" }, |
| 168 { 0, 300, 100, -10, 1, 0, 4, "", "0 1 2 3 4 94 184 199 200" }, |
| 169 }; |
| 170 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { |
| 171 CreateLayout(test_data[i]); |
| 172 EXPECT_EQ(test_data[i].expected_bounds, BoundsString()) << " at " << i; |
| 173 } |
| 174 } |
| 175 |
| 176 // Assertions around dragging the active tab to the right. |
| 177 TEST_F(TouchTabStripLayoutTest, DragActiveTab) { |
| 178 struct TestData { |
| 179 struct CommonTestData common_data; |
| 180 const int delta; |
| 181 } test_data[] = { |
| 182 { { 0, 300, 100, -10, 2, 0, 4, "", "0 90 180 192 194 196 198 200" }, 1000 }, |
| 183 { { 0, 120, 100, -10, 2, 0, 3, "", "0 3 10 12 14 16 18 20" }, 13 }, |
| 184 { { 0, 200, 100, -10, 2, 0, 0, "", "0 85 92 94 96 98 100 100" }, -5 }, |
| 185 { { 0, 300, 100, -10, 2, 0, 4, "", "0 2 4 18 108 196 198 200" }, 100 }, |
| 186 { { 0, 300, 100, -10, 2, 0, 5, "", "0 2 4 6 8 21 111 200" }, 1 }, |
| 187 { { 0, 120, 100, -10, 2, 0, 3, "", "0 8 10 12 14 16 18 20" }, 95 }, |
| 188 { { 0, 300, 100, -10, 2, 0, 5, "", "0 90 180 192 194 196 198 200" }, 1000 }, |
| 189 { { 0, 300, 100, -10, 2, 0, 4, "", "0 2 4 7 97 187 198 200" }, 89 }, |
| 190 { { 0, 300, 100, -10, 2, 0, 4, "", "0 2 4 6 96 186 198 200" }, 88 }, |
| 191 { { 0, 300, 100, -10, 2, 0, 4, "", "0 2 4 8 98 188 198 200" }, 90 }, |
| 192 { { 0, 300, 100, -10, 2, 0, 6, "", "0 2 4 6 8 70 160 200" }, 50 }, |
| 193 }; |
| 194 |
| 195 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { |
| 196 CreateLayout(test_data[i].common_data); |
| 197 layout_->DragActiveTab(test_data[i].delta); |
| 198 EXPECT_EQ(test_data[i].common_data.expected_bounds,BoundsString()) << |
| 199 " at " << i; |
| 200 } |
| 201 } |
| 202 |
| 203 // Assertions for dragging from an existing configuration. |
| 204 TEST_F(TouchTabStripLayoutTest, DragActiveTabExisting) { |
| 205 struct TestData { |
| 206 struct CommonTestData common_data; |
| 207 const int delta; |
| 208 } test_data[] = { |
| 209 { { 0, 643, 160, -27, 6, 0, 2, "0 84 217 350 483", "0 84 217 350 483" }, |
| 210 -11 }, |
| 211 { { 0, 685, 160, -27, 6, 0, 3, "0 6 12 18 151 284 417 519 525", |
| 212 "0 6 12 18 150 283 416 519 525" }, |
| 213 -1 }, |
| 214 { { 0, 120, 100, -10, 2, 0, 3, "0 3 10 12 14 16 18 20", |
| 215 "0 4 10 12 14 16 18 20" }, |
| 216 1 }, |
| 217 }; |
| 218 |
| 219 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { |
| 220 CreateLayout(test_data[i].common_data); |
| 221 layout_->DragActiveTab(test_data[i].delta); |
| 222 EXPECT_EQ(test_data[i].common_data.expected_bounds, BoundsString()) << |
| 223 " at " << i; |
| 224 } |
| 225 } |
| 226 |
| 227 // Assertions for AddTab(). |
| 228 TEST_F(TouchTabStripLayoutTest, AddTab) { |
| 229 struct TestData { |
| 230 CommonTestData common_data; |
| 231 int add_index; |
| 232 bool add_active; |
| 233 bool add_mini; |
| 234 } test_data[] = { |
| 235 { { 4, 200, 100, -10, 2, 1, 2, "0 4 10 100", "0 0 8 10 100"}, |
| 236 1, false, true }, |
| 237 { { 4, 200, 100, -10, 2, 1, 2, "0 4 10 100", "0 0 8 98 100"}, |
| 238 1, true, true }, |
| 239 { { 4, 200, 100, -10, 2, 1, 2, "0 4 10 100", "0 0 8 98 100"}, |
| 240 0, true, true }, |
| 241 { { 0, 200, 100, -10, 2, 0, 2, "0 2 10 100", "0 4 94 98 100"}, |
| 242 0, true, true }, |
| 243 |
| 244 { { 0, 200, 100, -10, 2, 0, 0, "0 90 92 92 94 96 98 100", |
| 245 "0 0 0 2 4 6 8 98 100"}, |
| 246 7, true, false }, |
| 247 { { 0, 200, 100, -10, 2, 0, 7, "0 2 4 6 8 8 10 100", |
| 248 "0 0 2 4 6 8 96 98 100"}, |
| 249 5, true, false }, |
| 250 { { 0, 200, 100, -10, 2, 0, 7, "0 2 4 6 8 8 10 100", |
| 251 "0 2 4 6 8 94 96 98 100"}, |
| 252 4, true, false }, |
| 253 { { 0, 200, 100, -10, 2, 0, 2, "0 2 10 100", "0 2 10 98 100"}, |
| 254 2, true, false }, |
| 255 { { 0, 200, 100, -10, 2, 0, 2, "0 2 10 100", "0 2 4 10 100"}, |
| 256 4, true, false }, |
| 257 { { 0, 200, 100, -10, 2, 0, 2, "0 2 10 100", "0 90 96 98 100"}, |
| 258 0, true, false }, |
| 259 }; |
| 260 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { |
| 261 CreateLayout(test_data[i].common_data); |
| 262 int add_types = 0; |
| 263 if (test_data[i].add_active) |
| 264 add_types |= TouchTabStripLayout::kAddTypeActive; |
| 265 if (test_data[i].add_mini) |
| 266 add_types |= TouchTabStripLayout::kAddTypeMini; |
| 267 AddViewToViewModel(test_data[i].add_index); |
| 268 layout_->AddTab(test_data[i].add_index, add_types, |
| 269 test_data[i].common_data.initial_x + |
| 270 (test_data[i].add_mini ? 4 : 0)); |
| 271 EXPECT_EQ(test_data[i].common_data.expected_bounds, BoundsString()) << |
| 272 " at " << i; |
| 273 } |
| 274 } |
| 275 |
| 276 // Assertions around removing tabs. |
| 277 TEST_F(TouchTabStripLayoutTest, RemoveTab) { |
| 278 // TODO: add coverage of removing mini tabs! |
| 279 struct TestData { |
| 280 struct CommonTestData common_data; |
| 281 const int remove_index; |
| 282 const int x_after_remove; |
| 283 } test_data[] = { |
| 284 // Stacked tabs on both sides. |
| 285 { { 0, 200, 100, -10, 2, 0, 4, "0 2 4 6 8 10 80 98 100", |
| 286 "0 2 4 6 10 80 98 100" }, |
| 287 4, 0 }, |
| 288 |
| 289 // Mini-tabs. |
| 290 { { 8, 200, 100, -10, 2, 1, 0, "0 8 94 96 98 100", "0 86 88 90 100" }, |
| 291 0, 0 }, |
| 292 { { 16, 200, 100, -10, 2, 2, 0, "0 8 16 94 96 98 100", "8 8 86 88 90 100" }, |
| 293 0, 8 }, |
| 294 { { 16, 200, 100, -10, 2, 2, 0, "0 8 16 94 96 98 100", "0 8 86 88 90 100" }, |
| 295 1, 8 }, |
| 296 |
| 297 // Remove from ideal layout. |
| 298 { { 0, 200, 100, -10, 2, 0, 0, "0 90 94 96 98 100", "0 90 96 98 100" }, |
| 299 0, 0 }, |
| 300 { { 0, 200, 100, -10, 2, 0, 0, "0 90 94 96 98 100", "0 90 96 98 100" }, |
| 301 1, 0 }, |
| 302 { { 0, 200, 100, -10, 2, 0, 0, "0 90 94 96 98 100", "0 90 96 98 100" }, |
| 303 2, 0 }, |
| 304 { { 0, 200, 100, -10, 2, 0, 0, "0 90 94 96 98 100", "0 90 94 98 100" }, |
| 305 3, 0 }, |
| 306 { { 0, 200, 100, -10, 2, 0, 0, "0 90 94 96 98 100", "0 90 94 96 100" }, |
| 307 5, 0 }, |
| 308 }; |
| 309 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { |
| 310 CreateLayout(test_data[i].common_data); |
| 311 int old_x = view_model_.ideal_bounds(test_data[i].remove_index).x(); |
| 312 view_model_.Remove(test_data[i].remove_index); |
| 313 layout_->RemoveTab(test_data[i].remove_index, test_data[i].x_after_remove, |
| 314 old_x); |
| 315 EXPECT_EQ(test_data[i].common_data.expected_bounds, BoundsString()) << |
| 316 " at " << i; |
| 317 } |
| 318 } |
| 319 |
| 320 // Assertions for SetWidth(). |
| 321 TEST_F(TouchTabStripLayoutTest, SetWidth) { |
| 322 struct TestData { |
| 323 CommonTestData common_data; |
| 324 int new_width; |
| 325 } test_data[] = { |
| 326 { { 8, 250, 100, -10, 2, 2, 2, "0 4 8 98 148 150", "0 4 8 98 160 250"}, |
| 327 350 }, |
| 328 { { 8, 250, 100, -10, 2, 2, 2, "0 4 8 98 148 150", "0 4 8 96 98 100"}, |
| 329 200 }, |
| 330 |
| 331 { { 0, 250, 100, -10, 2, 0, 2, "0 40 90 120 150", "0 40 90 98 100"}, 200 }, |
| 332 { { 0, 250, 100, -10, 2, 0, 2, "0 2 60 150", "0 2 60 100"}, 200 }, |
| 333 { { 0, 250, 100, -10, 2, 0, 2, "0 40 120 150", "0 40 98 100"}, 200 }, |
| 334 |
| 335 { { 0, 200, 100, -10, 2, 0, 2, "0 2 10 100", "0 2 60 150"}, 250 }, |
| 336 { { 0, 200, 100, -10, 2, 0, 2, "0 2 4 10 100", "0 2 20 110 200"}, 300 }, |
| 337 }; |
| 338 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { |
| 339 CreateLayout(test_data[i].common_data); |
| 340 layout_->SetWidth(test_data[i].new_width); |
| 341 EXPECT_EQ(test_data[i].common_data.expected_bounds, BoundsString()) << |
| 342 " at " << i; |
| 343 } |
| 344 } |
| 345 |
| 346 // Assertions for SetActiveIndex(). |
| 347 TEST_F(TouchTabStripLayoutTest, SetActiveIndex) { |
| 348 struct TestData { |
| 349 CommonTestData common_data; |
| 350 int new_index; |
| 351 } test_data[] = { |
| 352 { { 0, 250, 100, -10, 2, 0, 2, "0 4 8 98 148 150", "0 90 144 146 148 150"}, |
| 353 0 }, |
| 354 { { 0, 250, 100, -10, 2, 0, 2, "0 4 8 98 148 150", "0 2 4 58 148 150"}, 4 }, |
| 355 { { 0, 250, 100, -10, 2, 0, 2, "0 4 8 98 148 150", "0 2 4 6 60 150"}, 5 }, |
| 356 { { 4, 250, 100, -10, 2, 1, 2, "0 4 8 98 148 150", "0 4 94 146 148 150"}, |
| 357 0 }, |
| 358 }; |
| 359 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { |
| 360 CreateLayout(test_data[i].common_data); |
| 361 layout_->SetActiveIndex(test_data[i].new_index); |
| 362 EXPECT_EQ(test_data[i].common_data.expected_bounds, BoundsString()) << |
| 363 " at " << i; |
| 364 } |
| 365 } |
| 366 |
| 367 TEST_F(TouchTabStripLayoutTest, Foo) { |
| 368 TouchTabStripLayout layout(gfx::Size(160, 10), -27, 6, 4, &view_model_); |
| 369 ASSERT_NO_FATAL_FAILURE(SetBoundsFromString("0 6 12 18 126 259 392 525")); |
| 370 Reset(&layout, 0, 685, 0, 5); |
| 371 ASSERT_NO_FATAL_FAILURE(SetBoundsFromString("0 6 12 18 126 259 392 525")); |
| 372 layout.DragActiveTab(11); |
| 373 layout.DragActiveTab(1); |
| 374 } |
OLD | NEW |