Home | History | Annotate | Download | only in shelf
      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/ash_switches.h"
     11 #include "ash/shelf/shelf_model_observer.h"
     12 #include "base/command_line.h"
     13 #include "base/strings/stringprintf.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 
     16 namespace ash {
     17 
     18 namespace {
     19 
     20 // ShelfModelObserver implementation that tracks what message are invoked.
     21 class TestShelfModelObserver : public ShelfModelObserver {
     22  public:
     23   TestShelfModelObserver()
     24       : added_count_(0),
     25         removed_count_(0),
     26         changed_count_(0),
     27         moved_count_(0) {
     28   }
     29 
     30   // Returns a string description of the changes that have occurred since this
     31   // was last invoked. Resets state to initial state.
     32   std::string StateStringAndClear() {
     33     std::string result;
     34     AddToResult("added=%d", added_count_, &result);
     35     AddToResult("removed=%d", removed_count_, &result);
     36     AddToResult("changed=%d", changed_count_, &result);
     37     AddToResult("moved=%d", moved_count_, &result);
     38     added_count_ = removed_count_ = changed_count_ = moved_count_ = 0;
     39     return result;
     40   }
     41 
     42   // ShelfModelObserver overrides:
     43   virtual void ShelfItemAdded(int index) OVERRIDE {
     44     added_count_++;
     45   }
     46   virtual void ShelfItemRemoved(int index, ShelfID id) OVERRIDE {
     47     removed_count_++;
     48   }
     49   virtual void ShelfItemChanged(int index,
     50                                 const ShelfItem& old_item) OVERRIDE {
     51     changed_count_++;
     52   }
     53   virtual void ShelfItemMoved(int start_index, int target_index) OVERRIDE {
     54     moved_count_++;
     55   }
     56   virtual void ShelfStatusChanged() OVERRIDE {
     57   }
     58 
     59  private:
     60   void AddToResult(const std::string& format, int count, std::string* result) {
     61     if (!count)
     62       return;
     63     if (!result->empty())
     64       *result += " ";
     65     *result += base::StringPrintf(format.c_str(), count);
     66   }
     67 
     68   int added_count_;
     69   int removed_count_;
     70   int changed_count_;
     71   int moved_count_;
     72 
     73   DISALLOW_COPY_AND_ASSIGN(TestShelfModelObserver);
     74 };
     75 
     76 }  // namespace
     77 
     78 class ShelfModelTest : public testing::Test {
     79  public:
     80   ShelfModelTest() {}
     81   virtual ~ShelfModelTest() {}
     82 
     83   virtual void SetUp() {
     84     model_.reset(new ShelfModel);
     85     observer_.reset(new TestShelfModelObserver);
     86     EXPECT_EQ(0, model_->item_count());
     87 
     88     ShelfItem item;
     89     item.type = TYPE_APP_LIST;
     90     model_->Add(item);
     91     EXPECT_EQ(1, model_->item_count());
     92 
     93     model_->AddObserver(observer_.get());
     94   }
     95 
     96   virtual void TearDown() {
     97     observer_.reset();
     98     model_.reset();
     99   }
    100 
    101   scoped_ptr<ShelfModel> model_;
    102   scoped_ptr<TestShelfModelObserver> observer_;
    103 
    104  private:
    105   DISALLOW_COPY_AND_ASSIGN(ShelfModelTest);
    106 };
    107 
    108 TEST_F(ShelfModelTest, BasicAssertions) {
    109   // Add an item.
    110   ShelfItem item;
    111   item.type = TYPE_APP_SHORTCUT;
    112   int index = model_->Add(item);
    113   EXPECT_EQ(2, model_->item_count());
    114   EXPECT_EQ("added=1", observer_->StateStringAndClear());
    115 
    116   // Change to a platform app item.
    117   ShelfID original_id = model_->items()[index].id;
    118   item.type = TYPE_PLATFORM_APP;
    119   model_->Set(index, item);
    120   EXPECT_EQ(original_id, model_->items()[index].id);
    121   EXPECT_EQ("changed=1", observer_->StateStringAndClear());
    122   EXPECT_EQ(TYPE_PLATFORM_APP, model_->items()[index].type);
    123 
    124   // Remove the item.
    125   model_->RemoveItemAt(index);
    126   EXPECT_EQ(1, model_->item_count());
    127   EXPECT_EQ("removed=1", observer_->StateStringAndClear());
    128 
    129   // Add an app item.
    130   item.type = TYPE_APP_SHORTCUT;
    131   index = model_->Add(item);
    132   observer_->StateStringAndClear();
    133 
    134   // Change everything.
    135   model_->Set(index, item);
    136   EXPECT_EQ("changed=1", observer_->StateStringAndClear());
    137   EXPECT_EQ(TYPE_APP_SHORTCUT, model_->items()[index].type);
    138 
    139   // Add another item.
    140   item.type = TYPE_APP_SHORTCUT;
    141   model_->Add(item);
    142   observer_->StateStringAndClear();
    143 
    144   // Move the second to the first.
    145   model_->Move(1, 0);
    146   EXPECT_EQ("moved=1", observer_->StateStringAndClear());
    147 
    148   // And back.
    149   model_->Move(0, 1);
    150   EXPECT_EQ("moved=1", observer_->StateStringAndClear());
    151 
    152   // Verifies all the items get unique ids.
    153   std::set<ShelfID> ids;
    154   for (int i = 0; i < model_->item_count(); ++i)
    155     ids.insert(model_->items()[i].id);
    156   EXPECT_EQ(model_->item_count(), static_cast<int>(ids.size()));
    157 }
    158 
    159 // Assertions around where items are added.
    160 TEST_F(ShelfModelTest, AddIndices) {
    161   // Insert browser short cut at index 1.
    162   ShelfItem browser_shortcut;
    163   browser_shortcut.type = TYPE_BROWSER_SHORTCUT;
    164   int browser_shortcut_index = model_->Add(browser_shortcut);
    165   EXPECT_EQ(1, browser_shortcut_index);
    166 
    167   // platform app items should be after browser shortcut.
    168   ShelfItem item;
    169   item.type = TYPE_PLATFORM_APP;
    170   int platform_app_index1 = model_->Add(item);
    171   EXPECT_EQ(2, platform_app_index1);
    172 
    173   // Add another platform app item, it should follow first.
    174   int platform_app_index2 = model_->Add(item);
    175   EXPECT_EQ(3, platform_app_index2);
    176 
    177   // APP_SHORTCUT priority is higher than PLATFORM_APP but same as
    178   // BROWSER_SHORTCUT. So APP_SHORTCUT is located after BROWSER_SHORCUT.
    179   item.type = TYPE_APP_SHORTCUT;
    180   int app_shortcut_index1 = model_->Add(item);
    181   EXPECT_EQ(2, app_shortcut_index1);
    182 
    183   item.type = TYPE_APP_SHORTCUT;
    184   int app_shortcut_index2 = model_->Add(item);
    185   EXPECT_EQ(3, app_shortcut_index2);
    186 
    187   // Check that AddAt() figures out the correct indexes for app shortcuts.
    188   // APP_SHORTCUT and BROWSER_SHORTCUT has the same weight.
    189   // So APP_SHORTCUT is located at index 0. And, BROWSER_SHORTCUT is located at
    190   // index 1.
    191   item.type = TYPE_APP_SHORTCUT;
    192   int app_shortcut_index3 = model_->AddAt(1, item);
    193   EXPECT_EQ(1, app_shortcut_index3);
    194 
    195   item.type = TYPE_APP_SHORTCUT;
    196   int app_shortcut_index4 = model_->AddAt(6, item);
    197   EXPECT_EQ(5, app_shortcut_index4);
    198 
    199   item.type = TYPE_APP_SHORTCUT;
    200   int app_shortcut_index5 = model_->AddAt(3, item);
    201   EXPECT_EQ(3, app_shortcut_index5);
    202 
    203   // Before there are any panels, no icons should be right aligned.
    204   EXPECT_EQ(model_->item_count(), model_->FirstPanelIndex());
    205 
    206   // Check that AddAt() figures out the correct indexes for platform apps and
    207   // panels.
    208   item.type = TYPE_PLATFORM_APP;
    209   int platform_app_index3 = model_->AddAt(3, item);
    210   EXPECT_EQ(7, platform_app_index3);
    211 
    212   item.type = TYPE_APP_PANEL;
    213   int app_panel_index1 = model_->AddAt(2, item);
    214   EXPECT_EQ(10, app_panel_index1);
    215 
    216   item.type = TYPE_PLATFORM_APP;
    217   int platform_app_index4 = model_->AddAt(11, item);
    218   EXPECT_EQ(10, platform_app_index4);
    219 
    220   item.type = TYPE_APP_PANEL;
    221   int app_panel_index2 = model_->AddAt(12, item);
    222   EXPECT_EQ(12, app_panel_index2);
    223 
    224   item.type = TYPE_PLATFORM_APP;
    225   int platform_app_index5 = model_->AddAt(7, item);
    226   EXPECT_EQ(7, platform_app_index5);
    227 
    228   item.type = TYPE_APP_PANEL;
    229   int app_panel_index3 = model_->AddAt(13, item);
    230   EXPECT_EQ(13, app_panel_index3);
    231 
    232   // Right aligned index should be the first app panel index.
    233   EXPECT_EQ(12, model_->FirstPanelIndex());
    234 
    235   EXPECT_EQ(TYPE_BROWSER_SHORTCUT, model_->items()[2].type);
    236   EXPECT_EQ(TYPE_APP_LIST, model_->items()[0].type);
    237 }
    238 
    239 // Test that the indexes for the running applications are properly determined
    240 // when the first running application is a windowed application.
    241 TEST_F(ShelfModelTest, FirstRunningAppIndexUsingWindowedAppFirst) {
    242   // Insert the browser shortcut at index 1 and check that the running
    243   // application index would be behind it.
    244   ShelfItem item;
    245   item.type = TYPE_BROWSER_SHORTCUT;
    246   EXPECT_EQ(1, model_->Add(item));
    247   EXPECT_EQ(2, model_->FirstRunningAppIndex());
    248 
    249   // Insert a panel application at the end and check that the running
    250   // application index would be at / before the application panel.
    251   item.type = TYPE_APP_PANEL;
    252   EXPECT_EQ(2, model_->Add(item));
    253   EXPECT_EQ(2, model_->FirstRunningAppIndex());
    254 
    255   // Insert an application shortcut and make sure that the running application
    256   // index would be behind it.
    257   item.type = TYPE_APP_SHORTCUT;
    258   EXPECT_EQ(2, model_->Add(item));
    259   EXPECT_EQ(3, model_->FirstRunningAppIndex());
    260 
    261   // Insert different running application shortcuts - but first a windowed
    262   // application - and make sure that the same index gets returned.
    263   item.type = TYPE_WINDOWED_APP;
    264   int running_app_index = model_->Add(item);
    265   EXPECT_EQ(3, running_app_index);
    266   EXPECT_EQ(running_app_index, model_->FirstRunningAppIndex());
    267   item.type = TYPE_PLATFORM_APP;
    268   EXPECT_EQ(running_app_index + 1, model_->Add(item));
    269   EXPECT_EQ(running_app_index, model_->FirstRunningAppIndex());
    270 }
    271 
    272 // Test that the indexes for the running applications are properly determined
    273 // when the first running application is a platform application.
    274 TEST_F(ShelfModelTest, FirstRunningAppIndexUsingPlatformAppFirst) {
    275   // Insert the browser shortcut at index 1 and check that the running
    276   // application index would be behind it.
    277   ShelfItem item;
    278   item.type = TYPE_BROWSER_SHORTCUT;
    279   EXPECT_EQ(1, model_->Add(item));
    280   EXPECT_EQ(2, model_->FirstRunningAppIndex());
    281 
    282   // Insert a panel application at the end and check that the running
    283   // application index would be at / before the application panel.
    284   item.type = TYPE_APP_PANEL;
    285   EXPECT_EQ(2, model_->Add(item));
    286   EXPECT_EQ(2, model_->FirstRunningAppIndex());
    287 
    288   // Insert an application shortcut and make sure that the running application
    289   // index would be behind it.
    290   item.type = TYPE_APP_SHORTCUT;
    291   EXPECT_EQ(2, model_->Add(item));
    292   EXPECT_EQ(3, model_->FirstRunningAppIndex());
    293 
    294   // Insert different running application shortcuts - but first a platfom
    295   // application - and make sure that the same index gets returned.
    296   item.type = TYPE_PLATFORM_APP;
    297   int running_app_index = model_->Add(item);
    298   EXPECT_EQ(3, running_app_index);
    299   EXPECT_EQ(running_app_index, model_->FirstRunningAppIndex());
    300   item.type = TYPE_WINDOWED_APP;
    301   EXPECT_EQ(running_app_index + 1, model_->Add(item));
    302   EXPECT_EQ(running_app_index, model_->FirstRunningAppIndex());
    303 }
    304 
    305 // Assertions around id generation and usage.
    306 TEST_F(ShelfModelTest, ShelfIDTests) {
    307   // Get the next to use ID counter.
    308   ShelfID id = model_->next_id();
    309 
    310   // Calling this function multiple times does not change the returned ID.
    311   EXPECT_EQ(model_->next_id(), id);
    312 
    313   // Check that when we reserve a value it will be the previously retrieved ID,
    314   // but it will not change the item count and retrieving the next ID should
    315   // produce something new.
    316   EXPECT_EQ(model_->reserve_external_id(), id);
    317   EXPECT_EQ(1, model_->item_count());
    318   ShelfID id2 = model_->next_id();
    319   EXPECT_NE(id2, id);
    320 
    321   // Adding another item to the list should also produce a new ID.
    322   ShelfItem item;
    323   item.type = TYPE_PLATFORM_APP;
    324   model_->Add(item);
    325   EXPECT_NE(model_->next_id(), id2);
    326 }
    327 
    328 // This verifies that converting an existing item into a lower weight category
    329 // (e.g. shortcut to running but not pinned app) will move it to the proper
    330 // location. See crbug.com/248769.
    331 TEST_F(ShelfModelTest, CorrectMoveItemsWhenStateChange) {
    332   // The first item is the app list and last item is the browser.
    333   ShelfItem browser_shortcut;
    334   browser_shortcut.type = TYPE_BROWSER_SHORTCUT;
    335   int browser_shortcut_index = model_->Add(browser_shortcut);
    336   EXPECT_EQ(TYPE_APP_LIST, model_->items()[0].type);
    337   EXPECT_EQ(1, browser_shortcut_index);
    338 
    339   // Add three shortcuts. They should all be moved between the two.
    340   ShelfItem item;
    341   item.type = TYPE_APP_SHORTCUT;
    342   int app1_index = model_->Add(item);
    343   EXPECT_EQ(2, app1_index);
    344   int app2_index = model_->Add(item);
    345   EXPECT_EQ(3, app2_index);
    346   int app3_index = model_->Add(item);
    347   EXPECT_EQ(4, app3_index);
    348 
    349   // Now change the type of the second item and make sure that it is moving
    350   // behind the shortcuts.
    351   item.type = TYPE_PLATFORM_APP;
    352   model_->Set(app2_index, item);
    353 
    354   // The item should have moved in front of the app launcher.
    355   EXPECT_EQ(TYPE_PLATFORM_APP, model_->items()[4].type);
    356 }
    357 
    358 }  // namespace ash
    359