| OLD | NEW |
| (Empty) |
| 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 "ash/shelf/shelf_model.h" | |
| 6 | |
| 7 #include <set> | |
| 8 #include <string> | |
| 9 | |
| 10 #include "ash/shelf/shelf_model_observer.h" | |
| 11 #include "base/strings/stringprintf.h" | |
| 12 #include "testing/gtest/include/gtest/gtest.h" | |
| 13 | |
| 14 namespace ash { | |
| 15 | |
| 16 namespace { | |
| 17 | |
| 18 // ShelfModelObserver implementation that tracks what message are invoked. | |
| 19 class TestShelfModelObserver : public ShelfModelObserver { | |
| 20 public: | |
| 21 TestShelfModelObserver() {} | |
| 22 | |
| 23 // Returns a string description of the changes that have occurred since this | |
| 24 // was last invoked. Resets state to initial state. | |
| 25 std::string StateStringAndClear() { | |
| 26 std::string result; | |
| 27 AddToResult("added=%d", added_count_, &result); | |
| 28 AddToResult("removed=%d", removed_count_, &result); | |
| 29 AddToResult("changed=%d", changed_count_, &result); | |
| 30 AddToResult("moved=%d", moved_count_, &result); | |
| 31 added_count_ = removed_count_ = changed_count_ = moved_count_ = 0; | |
| 32 return result; | |
| 33 } | |
| 34 | |
| 35 // ShelfModelObserver overrides: | |
| 36 void ShelfItemAdded(int) override { added_count_++; } | |
| 37 void ShelfItemRemoved(int, const ShelfItem&) override { removed_count_++; } | |
| 38 void ShelfItemChanged(int, const ShelfItem&) override { changed_count_++; } | |
| 39 void ShelfItemMoved(int, int) override { moved_count_++; } | |
| 40 void ShelfItemDelegateChanged(const ShelfID&, ShelfItemDelegate*) override {} | |
| 41 | |
| 42 private: | |
| 43 void AddToResult(const std::string& format, int count, std::string* result) { | |
| 44 if (!count) | |
| 45 return; | |
| 46 if (!result->empty()) | |
| 47 *result += " "; | |
| 48 *result += base::StringPrintf(format.c_str(), count); | |
| 49 } | |
| 50 | |
| 51 int added_count_ = 0; | |
| 52 int removed_count_ = 0; | |
| 53 int changed_count_ = 0; | |
| 54 int moved_count_ = 0; | |
| 55 | |
| 56 DISALLOW_COPY_AND_ASSIGN(TestShelfModelObserver); | |
| 57 }; | |
| 58 | |
| 59 } // namespace | |
| 60 | |
| 61 class ShelfModelTest : public testing::Test { | |
| 62 public: | |
| 63 ShelfModelTest() {} | |
| 64 ~ShelfModelTest() override {} | |
| 65 | |
| 66 void SetUp() override { | |
| 67 model_.reset(new ShelfModel); | |
| 68 observer_.reset(new TestShelfModelObserver); | |
| 69 model_->AddObserver(observer_.get()); | |
| 70 } | |
| 71 | |
| 72 void TearDown() override { | |
| 73 observer_.reset(); | |
| 74 model_.reset(); | |
| 75 } | |
| 76 | |
| 77 std::unique_ptr<ShelfModel> model_; | |
| 78 std::unique_ptr<TestShelfModelObserver> observer_; | |
| 79 | |
| 80 private: | |
| 81 DISALLOW_COPY_AND_ASSIGN(ShelfModelTest); | |
| 82 }; | |
| 83 | |
| 84 TEST_F(ShelfModelTest, IntializesAppListItem) { | |
| 85 EXPECT_EQ(1, model_->item_count()); | |
| 86 EXPECT_EQ(kAppListId, model_->items()[0].id.app_id); | |
| 87 // The ShelfModel does not initialize the AppList's ShelfItemDelegate. | |
| 88 // ShelfController does that to prevent Chrome from creating its own delegate. | |
| 89 EXPECT_FALSE(model_->GetShelfItemDelegate(ShelfID(kAppListId))); | |
| 90 } | |
| 91 | |
| 92 TEST_F(ShelfModelTest, BasicAssertions) { | |
| 93 // Add an item. | |
| 94 ShelfItem item1; | |
| 95 item1.id = ShelfID("item1"); | |
| 96 item1.type = TYPE_PINNED_APP; | |
| 97 int index = model_->Add(item1); | |
| 98 EXPECT_EQ(2, model_->item_count()); | |
| 99 EXPECT_LE(0, model_->ItemIndexByID(item1.id)); | |
| 100 EXPECT_NE(model_->items().end(), model_->ItemByID(item1.id)); | |
| 101 EXPECT_EQ("added=1", observer_->StateStringAndClear()); | |
| 102 | |
| 103 // Change to a platform app item. | |
| 104 item1.type = TYPE_APP; | |
| 105 model_->Set(index, item1); | |
| 106 EXPECT_EQ(item1.id, model_->items()[index].id); | |
| 107 EXPECT_LE(0, model_->ItemIndexByID(item1.id)); | |
| 108 EXPECT_NE(model_->items().end(), model_->ItemByID(item1.id)); | |
| 109 EXPECT_EQ("changed=1", observer_->StateStringAndClear()); | |
| 110 EXPECT_EQ(TYPE_APP, model_->items()[index].type); | |
| 111 | |
| 112 // Remove the item. | |
| 113 model_->RemoveItemAt(index); | |
| 114 EXPECT_EQ(1, model_->item_count()); | |
| 115 EXPECT_EQ(-1, model_->ItemIndexByID(item1.id)); | |
| 116 EXPECT_EQ(model_->items().end(), model_->ItemByID(item1.id)); | |
| 117 EXPECT_EQ("removed=1", observer_->StateStringAndClear()); | |
| 118 | |
| 119 // Add an app item. | |
| 120 ShelfItem item2; | |
| 121 item2.id = ShelfID("item2"); | |
| 122 item2.type = TYPE_PINNED_APP; | |
| 123 index = model_->Add(item2); | |
| 124 EXPECT_EQ(2, model_->item_count()); | |
| 125 EXPECT_LE(0, model_->ItemIndexByID(item2.id)); | |
| 126 EXPECT_NE(model_->items().end(), model_->ItemByID(item2.id)); | |
| 127 EXPECT_EQ("added=1", observer_->StateStringAndClear()); | |
| 128 | |
| 129 // Change the item type. | |
| 130 item2.type = TYPE_APP; | |
| 131 model_->Set(index, item2); | |
| 132 EXPECT_LE(0, model_->ItemIndexByID(item2.id)); | |
| 133 EXPECT_NE(model_->items().end(), model_->ItemByID(item2.id)); | |
| 134 EXPECT_EQ("changed=1", observer_->StateStringAndClear()); | |
| 135 EXPECT_EQ(TYPE_APP, model_->items()[index].type); | |
| 136 | |
| 137 // Add another item. | |
| 138 ShelfItem item3; | |
| 139 item3.id = ShelfID("item3"); | |
| 140 item3.type = TYPE_PINNED_APP; | |
| 141 model_->Add(item3); | |
| 142 EXPECT_EQ(3, model_->item_count()); | |
| 143 EXPECT_LE(0, model_->ItemIndexByID(item3.id)); | |
| 144 EXPECT_NE(model_->items().end(), model_->ItemByID(item3.id)); | |
| 145 EXPECT_EQ("added=1", observer_->StateStringAndClear()); | |
| 146 | |
| 147 // Move the second to the first. | |
| 148 model_->Move(1, 0); | |
| 149 EXPECT_EQ("moved=1", observer_->StateStringAndClear()); | |
| 150 | |
| 151 // And back. | |
| 152 model_->Move(0, 1); | |
| 153 EXPECT_EQ("moved=1", observer_->StateStringAndClear()); | |
| 154 | |
| 155 // Verifies all the items get unique ids. | |
| 156 std::set<ShelfID> ids; | |
| 157 for (int i = 0; i < model_->item_count(); ++i) | |
| 158 ids.insert(model_->items()[i].id); | |
| 159 EXPECT_EQ(model_->item_count(), static_cast<int>(ids.size())); | |
| 160 } | |
| 161 | |
| 162 // Assertions around where items are added. | |
| 163 TEST_F(ShelfModelTest, AddIndices) { | |
| 164 // Insert browser short cut at index 1. | |
| 165 ShelfItem browser_shortcut; | |
| 166 browser_shortcut.id = ShelfID("browser"); | |
| 167 browser_shortcut.type = TYPE_BROWSER_SHORTCUT; | |
| 168 int browser_shortcut_index = model_->Add(browser_shortcut); | |
| 169 EXPECT_EQ(1, browser_shortcut_index); | |
| 170 | |
| 171 // App items should be after the browser shortcut. | |
| 172 ShelfItem item; | |
| 173 item.type = TYPE_APP; | |
| 174 item.id = ShelfID("id1"); | |
| 175 int platform_app_index1 = model_->Add(item); | |
| 176 EXPECT_EQ(2, platform_app_index1); | |
| 177 | |
| 178 // Add another platform app item, it should follow first. | |
| 179 item.id = ShelfID("id2"); | |
| 180 int platform_app_index2 = model_->Add(item); | |
| 181 EXPECT_EQ(3, platform_app_index2); | |
| 182 | |
| 183 // TYPE_PINNED_APP priority is higher than TYPE_APP but same as | |
| 184 // TYPE_BROWSER_SHORTCUT. So TYPE_PINNED_APP is located after | |
| 185 // TYPE_BROWSER_SHORTCUT. | |
| 186 item.type = TYPE_PINNED_APP; | |
| 187 item.id = ShelfID("id3"); | |
| 188 int app_shortcut_index1 = model_->Add(item); | |
| 189 EXPECT_EQ(2, app_shortcut_index1); | |
| 190 | |
| 191 item.type = TYPE_PINNED_APP; | |
| 192 item.id = ShelfID("id4"); | |
| 193 int app_shortcut_index2 = model_->Add(item); | |
| 194 EXPECT_EQ(3, app_shortcut_index2); | |
| 195 | |
| 196 // Check that AddAt() figures out the correct indexes for app shortcuts. | |
| 197 // TYPE_PINNED_APP and TYPE_BROWSER_SHORTCUT has the same weight. | |
| 198 // So TYPE_PINNED_APP is located at index 0. And, TYPE_BROWSER_SHORTCUT is | |
| 199 // located at index 1. | |
| 200 item.type = TYPE_PINNED_APP; | |
| 201 item.id = ShelfID("id5"); | |
| 202 int app_shortcut_index3 = model_->AddAt(1, item); | |
| 203 EXPECT_EQ(1, app_shortcut_index3); | |
| 204 | |
| 205 item.type = TYPE_PINNED_APP; | |
| 206 item.id = ShelfID("id6"); | |
| 207 int app_shortcut_index4 = model_->AddAt(6, item); | |
| 208 EXPECT_EQ(5, app_shortcut_index4); | |
| 209 | |
| 210 item.type = TYPE_PINNED_APP; | |
| 211 item.id = ShelfID("id7"); | |
| 212 int app_shortcut_index5 = model_->AddAt(3, item); | |
| 213 EXPECT_EQ(3, app_shortcut_index5); | |
| 214 | |
| 215 // Before there are any panels, no icons should be right aligned. | |
| 216 EXPECT_EQ(model_->item_count(), model_->FirstPanelIndex()); | |
| 217 | |
| 218 // Check that AddAt() figures out the correct indexes for apps and panels. | |
| 219 item.type = TYPE_APP; | |
| 220 item.id = ShelfID("id8"); | |
| 221 int platform_app_index3 = model_->AddAt(3, item); | |
| 222 EXPECT_EQ(7, platform_app_index3); | |
| 223 | |
| 224 item.type = TYPE_APP_PANEL; | |
| 225 item.id = ShelfID("id9"); | |
| 226 int app_panel_index1 = model_->AddAt(2, item); | |
| 227 EXPECT_EQ(10, app_panel_index1); | |
| 228 | |
| 229 item.type = TYPE_APP; | |
| 230 item.id = ShelfID("id10"); | |
| 231 int platform_app_index4 = model_->AddAt(11, item); | |
| 232 EXPECT_EQ(10, platform_app_index4); | |
| 233 | |
| 234 item.type = TYPE_APP_PANEL; | |
| 235 item.id = ShelfID("id11"); | |
| 236 int app_panel_index2 = model_->AddAt(12, item); | |
| 237 EXPECT_EQ(12, app_panel_index2); | |
| 238 | |
| 239 item.type = TYPE_APP; | |
| 240 item.id = ShelfID("id12"); | |
| 241 int platform_app_index5 = model_->AddAt(7, item); | |
| 242 EXPECT_EQ(7, platform_app_index5); | |
| 243 | |
| 244 item.type = TYPE_APP_PANEL; | |
| 245 item.id = ShelfID("id13"); | |
| 246 int app_panel_index3 = model_->AddAt(13, item); | |
| 247 EXPECT_EQ(13, app_panel_index3); | |
| 248 | |
| 249 // Right aligned index should be the first app panel index. | |
| 250 EXPECT_EQ(12, model_->FirstPanelIndex()); | |
| 251 | |
| 252 EXPECT_EQ(TYPE_BROWSER_SHORTCUT, model_->items()[2].type); | |
| 253 EXPECT_EQ(TYPE_APP_LIST, model_->items()[0].type); | |
| 254 } | |
| 255 | |
| 256 // Test that the indexes for the running applications are properly determined. | |
| 257 TEST_F(ShelfModelTest, FirstRunningAppIndex) { | |
| 258 // Insert the browser shortcut at index 1 and check that the running | |
| 259 // application index would be behind it. | |
| 260 ShelfItem item; | |
| 261 item.id = ShelfID("browser"); | |
| 262 item.type = TYPE_BROWSER_SHORTCUT; | |
| 263 EXPECT_EQ(1, model_->Add(item)); | |
| 264 EXPECT_EQ(2, model_->FirstRunningAppIndex()); | |
| 265 | |
| 266 // Insert a panel application at the end and check that the running | |
| 267 // application index would be at / before the application panel. | |
| 268 item.type = TYPE_APP_PANEL; | |
| 269 item.id = ShelfID("app panel"); | |
| 270 EXPECT_EQ(2, model_->Add(item)); | |
| 271 EXPECT_EQ(2, model_->FirstRunningAppIndex()); | |
| 272 | |
| 273 // Insert an application shortcut and make sure that the running application | |
| 274 // index would be behind it. | |
| 275 item.type = TYPE_PINNED_APP; | |
| 276 item.id = ShelfID("pinned app"); | |
| 277 EXPECT_EQ(2, model_->Add(item)); | |
| 278 EXPECT_EQ(3, model_->FirstRunningAppIndex()); | |
| 279 | |
| 280 // Insert a two app items and check the first running app index. | |
| 281 item.type = TYPE_APP; | |
| 282 item.id = ShelfID("app1"); | |
| 283 EXPECT_EQ(3, model_->Add(item)); | |
| 284 EXPECT_EQ(3, model_->FirstRunningAppIndex()); | |
| 285 item.id = ShelfID("app2"); | |
| 286 EXPECT_EQ(4, model_->Add(item)); | |
| 287 EXPECT_EQ(3, model_->FirstRunningAppIndex()); | |
| 288 } | |
| 289 | |
| 290 // Test item reordering on type/weight (eg. pinning) changes. crbug.com/248769. | |
| 291 TEST_F(ShelfModelTest, ReorderOnTypeChanges) { | |
| 292 EXPECT_EQ(TYPE_APP_LIST, model_->items()[0].type); | |
| 293 | |
| 294 // Add three pinned items. | |
| 295 ShelfItem item1; | |
| 296 item1.type = TYPE_PINNED_APP; | |
| 297 item1.id = ShelfID("id1"); | |
| 298 int app1_index = model_->Add(item1); | |
| 299 EXPECT_EQ(1, app1_index); | |
| 300 | |
| 301 ShelfItem item2; | |
| 302 item2.type = TYPE_PINNED_APP; | |
| 303 item2.id = ShelfID("id2"); | |
| 304 int app2_index = model_->Add(item2); | |
| 305 EXPECT_EQ(2, app2_index); | |
| 306 | |
| 307 ShelfItem item3; | |
| 308 item3.type = TYPE_PINNED_APP; | |
| 309 item3.id = ShelfID("id3"); | |
| 310 int app3_index = model_->Add(item3); | |
| 311 EXPECT_EQ(3, app3_index); | |
| 312 | |
| 313 // Unpinning an item moves it behind the shortcuts. | |
| 314 EXPECT_EQ(item3.id, model_->items()[3].id); | |
| 315 item2.type = TYPE_APP; | |
| 316 model_->Set(app2_index, item2); | |
| 317 EXPECT_EQ(item2.id, model_->items()[3].id); | |
| 318 } | |
| 319 | |
| 320 // Test getting the index of ShelfIDs as a check for item presence. | |
| 321 TEST_F(ShelfModelTest, ItemIndexByID) { | |
| 322 // Expect empty and unknown ids to return the invalid index -1. | |
| 323 EXPECT_EQ(-1, model_->ItemIndexByID(ShelfID())); | |
| 324 EXPECT_EQ(-1, model_->ItemIndexByID(ShelfID("foo"))); | |
| 325 EXPECT_EQ(-1, model_->ItemIndexByID(ShelfID("foo", "bar"))); | |
| 326 | |
| 327 // Add an item and expect to get a valid index for its id. | |
| 328 ShelfItem item1; | |
| 329 item1.type = TYPE_PINNED_APP; | |
| 330 item1.id = ShelfID("app_id1", "launch_id1"); | |
| 331 const int index1 = model_->Add(item1); | |
| 332 EXPECT_EQ(index1, model_->ItemIndexByID(item1.id)); | |
| 333 | |
| 334 // Add another item and expect to get another valid index for its id. | |
| 335 ShelfItem item2; | |
| 336 item2.type = TYPE_APP; | |
| 337 item2.id = ShelfID("app_id2", "launch_id2"); | |
| 338 const int index2 = model_->Add(item2); | |
| 339 EXPECT_EQ(index2, model_->ItemIndexByID(item2.id)); | |
| 340 | |
| 341 // Removing the first item should yield an invalid index for that item. | |
| 342 model_->RemoveItemAt(index1); | |
| 343 EXPECT_EQ(-1, model_->ItemIndexByID(item1.id)); | |
| 344 // The index of the second item should be decremented, but still valid. | |
| 345 EXPECT_EQ(index2 - 1, model_->ItemIndexByID(item2.id)); | |
| 346 EXPECT_LE(0, model_->ItemIndexByID(item2.id)); | |
| 347 } | |
| 348 | |
| 349 // Test pinning and unpinning a closed app, and checking if it is pinned. | |
| 350 TEST_F(ShelfModelTest, ClosedAppPinning) { | |
| 351 const std::string app_id("app_id"); | |
| 352 | |
| 353 // Check the initial state. | |
| 354 EXPECT_FALSE(model_->IsAppPinned(app_id)); | |
| 355 EXPECT_EQ(1, model_->item_count()); | |
| 356 | |
| 357 // Pinning a previously unknown app should add an item. | |
| 358 model_->PinAppWithID(app_id); | |
| 359 EXPECT_TRUE(model_->IsAppPinned(app_id)); | |
| 360 EXPECT_EQ(2, model_->item_count()); | |
| 361 EXPECT_EQ(TYPE_PINNED_APP, model_->items()[1].type); | |
| 362 EXPECT_EQ(app_id, model_->items()[1].id.app_id); | |
| 363 | |
| 364 // Pinning the same app id again should have no change. | |
| 365 model_->PinAppWithID(app_id); | |
| 366 EXPECT_TRUE(model_->IsAppPinned(app_id)); | |
| 367 EXPECT_EQ(2, model_->item_count()); | |
| 368 EXPECT_EQ(TYPE_PINNED_APP, model_->items()[1].type); | |
| 369 EXPECT_EQ(app_id, model_->items()[1].id.app_id); | |
| 370 | |
| 371 // Unpinning the app should remove the item. | |
| 372 model_->UnpinAppWithID(app_id); | |
| 373 EXPECT_FALSE(model_->IsAppPinned(app_id)); | |
| 374 EXPECT_EQ(1, model_->item_count()); | |
| 375 | |
| 376 // Unpinning the same app id again should have no change. | |
| 377 model_->UnpinAppWithID(app_id); | |
| 378 EXPECT_FALSE(model_->IsAppPinned(app_id)); | |
| 379 EXPECT_EQ(1, model_->item_count()); | |
| 380 } | |
| 381 | |
| 382 // Test pinning and unpinning a running app, and checking if it is pinned. | |
| 383 TEST_F(ShelfModelTest, RunningAppPinning) { | |
| 384 const std::string app_id("app_id"); | |
| 385 | |
| 386 // Check the initial state. | |
| 387 EXPECT_FALSE(model_->IsAppPinned(app_id)); | |
| 388 EXPECT_EQ(1, model_->item_count()); | |
| 389 | |
| 390 // Add an example running app. | |
| 391 ShelfItem item; | |
| 392 item.type = TYPE_APP; | |
| 393 item.status = STATUS_RUNNING; | |
| 394 item.id = ShelfID(app_id); | |
| 395 const int index = model_->Add(item); | |
| 396 | |
| 397 // The item should be added but not pinned. | |
| 398 EXPECT_FALSE(model_->IsAppPinned(app_id)); | |
| 399 EXPECT_EQ(2, model_->item_count()); | |
| 400 EXPECT_EQ(TYPE_APP, model_->items()[index].type); | |
| 401 EXPECT_EQ(item.id, model_->items()[index].id); | |
| 402 | |
| 403 // Pinning the item should just change its type. | |
| 404 model_->PinAppWithID(app_id); | |
| 405 EXPECT_TRUE(model_->IsAppPinned(app_id)); | |
| 406 EXPECT_EQ(2, model_->item_count()); | |
| 407 EXPECT_EQ(TYPE_PINNED_APP, model_->items()[index].type); | |
| 408 EXPECT_EQ(item.id, model_->items()[index].id); | |
| 409 | |
| 410 // Pinning the same app id again should have no change. | |
| 411 model_->PinAppWithID(app_id); | |
| 412 EXPECT_TRUE(model_->IsAppPinned(app_id)); | |
| 413 EXPECT_EQ(2, model_->item_count()); | |
| 414 EXPECT_EQ(TYPE_PINNED_APP, model_->items()[index].type); | |
| 415 EXPECT_EQ(item.id, model_->items()[index].id); | |
| 416 | |
| 417 // Unpinning the app should leave the item unpinnned but running. | |
| 418 model_->UnpinAppWithID(app_id); | |
| 419 EXPECT_FALSE(model_->IsAppPinned(app_id)); | |
| 420 EXPECT_EQ(2, model_->item_count()); | |
| 421 EXPECT_EQ(TYPE_APP, model_->items()[index].type); | |
| 422 EXPECT_EQ(item.id, model_->items()[index].id); | |
| 423 | |
| 424 // Unpinning the same app id again should have no change. | |
| 425 model_->UnpinAppWithID(app_id); | |
| 426 EXPECT_FALSE(model_->IsAppPinned(app_id)); | |
| 427 EXPECT_EQ(2, model_->item_count()); | |
| 428 EXPECT_EQ(TYPE_APP, model_->items()[index].type); | |
| 429 EXPECT_EQ(item.id, model_->items()[index].id); | |
| 430 } | |
| 431 | |
| 432 } // namespace ash | |
| OLD | NEW |