// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/app_list/app_list_item_list.h" #include "base/memory/scoped_ptr.h" #include "base/strings/stringprintf.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/app_list/app_list_folder_item.h" #include "ui/app_list/app_list_item.h" #include "ui/app_list/app_list_item_list_observer.h" namespace app_list { namespace { class TestObserver : public AppListItemListObserver { public: TestObserver() : items_added_(0), items_removed_(0), items_moved_(0) {} ~TestObserver() override {} // AppListItemListObserver overriden: void OnListItemAdded(size_t index, AppListItem* item) override { ++items_added_; } void OnListItemRemoved(size_t index, AppListItem* item) override { ++items_removed_; } void OnListItemMoved(size_t from_index, size_t to_index, AppListItem* item) override { ++items_moved_; } size_t items_added() const { return items_added_; } size_t items_removed() const { return items_removed_; } size_t items_moved() const { return items_moved_; } void ResetCounts() { items_added_ = 0; items_removed_ = 0; items_moved_ = 0; } private: size_t items_added_; size_t items_removed_; size_t items_moved_; DISALLOW_COPY_AND_ASSIGN(TestObserver); }; std::string GetItemId(int id) { return base::StringPrintf("Item %d", id); } } // namespace class AppListItemListTest : public testing::Test { public: AppListItemListTest() {} ~AppListItemListTest() override {} // testing::Test overrides: void SetUp() override { item_list_.AddObserver(&observer_); } void TearDown() override { item_list_.RemoveObserver(&observer_); } protected: AppListItem* FindItem(const std::string& id) { return item_list_.FindItem(id); } bool FindItemIndex(const std::string& id, size_t* index) { return item_list_.FindItemIndex(id, index); } scoped_ptr CreateItem(const std::string& name) { scoped_ptr item(new AppListItem(name)); size_t nitems = item_list_.item_count(); syncer::StringOrdinal position; if (nitems == 0) position = syncer::StringOrdinal::CreateInitialOrdinal(); else position = item_list_.item_at(nitems - 1)->position().CreateAfter(); item->set_position(position); return item.Pass(); } AppListItem* CreateAndAddItem(const std::string& name) { scoped_ptr item(CreateItem(name)); return item_list_.AddItem(item.Pass()); } scoped_ptr RemoveItem(const std::string& id) { return item_list_.RemoveItem(id); } scoped_ptr RemoveItemAt(size_t index) { return item_list_.RemoveItemAt(index); } syncer::StringOrdinal CreatePositionBefore( const syncer::StringOrdinal& position) { return item_list_.CreatePositionBefore(position); } bool VerifyItemListOrdinals() { bool res = true; for (size_t i = 1; i < item_list_.item_count(); ++i) { res &= (item_list_.item_at(i - 1)->position().LessThan( item_list_.item_at(i)->position())); } if (!res) PrintItems(); return res; } bool VerifyItemOrder4(size_t a, size_t b, size_t c, size_t d) { if ((GetItemId(a) == item_list_.item_at(0)->id()) && (GetItemId(b) == item_list_.item_at(1)->id()) && (GetItemId(c) == item_list_.item_at(2)->id()) && (GetItemId(d) == item_list_.item_at(3)->id())) return true; PrintItems(); return false; } void PrintItems() { VLOG(1) << "ITEMS:"; for (size_t i = 0; i < item_list_.item_count(); ++i) VLOG(1) << " " << item_list_.item_at(i)->ToDebugString(); } AppListItemList item_list_; TestObserver observer_; private: DISALLOW_COPY_AND_ASSIGN(AppListItemListTest); }; TEST_F(AppListItemListTest, FindItemIndex) { AppListItem* item_0 = CreateAndAddItem(GetItemId(0)); AppListItem* item_1 = CreateAndAddItem(GetItemId(1)); AppListItem* item_2 = CreateAndAddItem(GetItemId(2)); EXPECT_EQ(observer_.items_added(), 3u); EXPECT_EQ(item_list_.item_count(), 3u); EXPECT_EQ(item_0, item_list_.item_at(0)); EXPECT_EQ(item_1, item_list_.item_at(1)); EXPECT_EQ(item_2, item_list_.item_at(2)); EXPECT_TRUE(VerifyItemListOrdinals()); size_t index; EXPECT_TRUE(FindItemIndex(item_0->id(), &index)); EXPECT_EQ(index, 0u); EXPECT_TRUE(FindItemIndex(item_1->id(), &index)); EXPECT_EQ(index, 1u); EXPECT_TRUE(FindItemIndex(item_2->id(), &index)); EXPECT_EQ(index, 2u); scoped_ptr item_3(CreateItem(GetItemId(3))); EXPECT_FALSE(FindItemIndex(item_3->id(), &index)); } TEST_F(AppListItemListTest, RemoveItemAt) { AppListItem* item_0 = CreateAndAddItem(GetItemId(0)); AppListItem* item_1 = CreateAndAddItem(GetItemId(1)); AppListItem* item_2 = CreateAndAddItem(GetItemId(2)); EXPECT_EQ(item_list_.item_count(), 3u); EXPECT_EQ(observer_.items_added(), 3u); size_t index; EXPECT_TRUE(FindItemIndex(item_1->id(), &index)); EXPECT_EQ(index, 1u); EXPECT_TRUE(VerifyItemListOrdinals()); scoped_ptr item_removed = RemoveItemAt(1); EXPECT_EQ(item_removed.get(), item_1); EXPECT_FALSE(FindItem(item_1->id())); EXPECT_EQ(item_list_.item_count(), 2u); EXPECT_EQ(observer_.items_removed(), 1u); EXPECT_EQ(item_list_.item_at(0), item_0); EXPECT_EQ(item_list_.item_at(1), item_2); EXPECT_TRUE(VerifyItemListOrdinals()); } TEST_F(AppListItemListTest, RemoveItem) { AppListItem* item_0 = CreateAndAddItem(GetItemId(0)); AppListItem* item_1 = CreateAndAddItem(GetItemId(1)); AppListItem* item_2 = CreateAndAddItem(GetItemId(2)); EXPECT_EQ(item_list_.item_count(), 3u); EXPECT_EQ(observer_.items_added(), 3u); EXPECT_EQ(item_0, item_list_.item_at(0)); EXPECT_EQ(item_1, item_list_.item_at(1)); EXPECT_EQ(item_2, item_list_.item_at(2)); EXPECT_TRUE(VerifyItemListOrdinals()); size_t index; EXPECT_TRUE(FindItemIndex(item_1->id(), &index)); EXPECT_EQ(index, 1u); scoped_ptr item_removed = RemoveItem(item_1->id()); EXPECT_EQ(item_removed.get(), item_1); EXPECT_FALSE(FindItem(item_1->id())); EXPECT_EQ(item_list_.item_count(), 2u); EXPECT_EQ(observer_.items_removed(), 1u); EXPECT_TRUE(VerifyItemListOrdinals()); } TEST_F(AppListItemListTest, MoveItem) { CreateAndAddItem(GetItemId(0)); CreateAndAddItem(GetItemId(1)); CreateAndAddItem(GetItemId(2)); CreateAndAddItem(GetItemId(3)); EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3)); item_list_.MoveItem(0, 0); EXPECT_EQ(0u, observer_.items_moved()); EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3)); item_list_.MoveItem(0, 1); EXPECT_EQ(1u, observer_.items_moved()); EXPECT_TRUE(VerifyItemListOrdinals()); EXPECT_TRUE(VerifyItemOrder4(1, 0, 2, 3)); item_list_.MoveItem(1, 2); EXPECT_EQ(2u, observer_.items_moved()); EXPECT_TRUE(VerifyItemListOrdinals()); EXPECT_TRUE(VerifyItemOrder4(1, 2, 0, 3)); item_list_.MoveItem(2, 1); EXPECT_EQ(3u, observer_.items_moved()); EXPECT_TRUE(VerifyItemListOrdinals()); EXPECT_TRUE(VerifyItemOrder4(1, 0, 2, 3)); item_list_.MoveItem(3, 0); EXPECT_EQ(4u, observer_.items_moved()); EXPECT_TRUE(VerifyItemListOrdinals()); EXPECT_TRUE(VerifyItemOrder4(3, 1, 0, 2)); item_list_.MoveItem(0, 3); EXPECT_EQ(5u, observer_.items_moved()); EXPECT_TRUE(VerifyItemListOrdinals()); EXPECT_TRUE(VerifyItemOrder4(1, 0, 2, 3)); } TEST_F(AppListItemListTest, SamePosition) { CreateAndAddItem(GetItemId(0)); CreateAndAddItem(GetItemId(1)); CreateAndAddItem(GetItemId(2)); CreateAndAddItem(GetItemId(3)); item_list_.SetItemPosition(item_list_.item_at(2), item_list_.item_at(1)->position()); EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3)); EXPECT_TRUE(item_list_.item_at(1)->position().Equals( item_list_.item_at(2)->position())); // Moving an item to position 1 should fix the position. observer_.ResetCounts(); item_list_.MoveItem(0, 1); EXPECT_TRUE(VerifyItemOrder4(1, 0, 2, 3)); EXPECT_TRUE(item_list_.item_at(0)->position().LessThan( item_list_.item_at(1)->position())); EXPECT_TRUE(item_list_.item_at(1)->position().LessThan( item_list_.item_at(2)->position())); EXPECT_TRUE(item_list_.item_at(2)->position().LessThan( item_list_.item_at(3)->position())); // One extra move notification for fixed position. EXPECT_EQ(2u, observer_.items_moved()); // Restore the original order. item_list_.MoveItem(1, 0); EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3)); // Set all four items to the same position. item_list_.SetItemPosition(item_list_.item_at(1), item_list_.item_at(0)->position()); item_list_.SetItemPosition(item_list_.item_at(2), item_list_.item_at(0)->position()); item_list_.SetItemPosition(item_list_.item_at(3), item_list_.item_at(0)->position()); // Move should fix the position of the items. observer_.ResetCounts(); item_list_.MoveItem(0, 1); EXPECT_TRUE(VerifyItemOrder4(1, 0, 2, 3)); EXPECT_TRUE(item_list_.item_at(0)->position().LessThan( item_list_.item_at(1)->position())); EXPECT_TRUE(item_list_.item_at(1)->position().LessThan( item_list_.item_at(2)->position())); EXPECT_TRUE(item_list_.item_at(2)->position().LessThan( item_list_.item_at(3)->position())); // One extra move notification for fixed position. EXPECT_EQ(2u, observer_.items_moved()); } TEST_F(AppListItemListTest, CreatePositionBefore) { CreateAndAddItem(GetItemId(0)); syncer::StringOrdinal position0 = item_list_.item_at(0)->position(); syncer::StringOrdinal new_position; new_position = CreatePositionBefore(position0.CreateBefore()); EXPECT_TRUE(new_position.LessThan(position0)); new_position = CreatePositionBefore(position0); EXPECT_TRUE(new_position.LessThan(position0)); new_position = CreatePositionBefore(position0.CreateAfter()); EXPECT_TRUE(new_position.GreaterThan(position0)); CreateAndAddItem(GetItemId(1)); syncer::StringOrdinal position1 = item_list_.item_at(1)->position(); EXPECT_TRUE(position1.GreaterThan(position0)); new_position = CreatePositionBefore(position1); EXPECT_TRUE(new_position.GreaterThan(position0)); EXPECT_TRUE(new_position.LessThan(position1)); // Invalid ordinal should return a position at the end of the list. new_position = CreatePositionBefore(syncer::StringOrdinal()); EXPECT_TRUE(new_position.GreaterThan(position1)); } TEST_F(AppListItemListTest, SetItemPosition) { CreateAndAddItem(GetItemId(0)); CreateAndAddItem(GetItemId(1)); CreateAndAddItem(GetItemId(2)); CreateAndAddItem(GetItemId(3)); EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3)); // No change to position. item_list_.SetItemPosition(item_list_.item_at(0), item_list_.item_at(0)->position()); EXPECT_TRUE(VerifyItemListOrdinals()); EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3)); // No order change. item_list_.SetItemPosition(item_list_.item_at(0), item_list_.item_at(0)->position().CreateBetween( item_list_.item_at(1)->position())); EXPECT_TRUE(VerifyItemListOrdinals()); EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3)); // 0 -> 1 item_list_.SetItemPosition(item_list_.item_at(0), item_list_.item_at(1)->position().CreateBetween( item_list_.item_at(2)->position())); EXPECT_TRUE(VerifyItemListOrdinals()); EXPECT_TRUE(VerifyItemOrder4(1, 0, 2, 3)); // 1 -> 2 item_list_.SetItemPosition(item_list_.item_at(1), item_list_.item_at(2)->position().CreateBetween( item_list_.item_at(3)->position())); EXPECT_TRUE(VerifyItemListOrdinals()); EXPECT_TRUE(VerifyItemOrder4(1, 2, 0, 3)); // 0 -> last item_list_.SetItemPosition(item_list_.item_at(0), item_list_.item_at(3)->position().CreateAfter()); EXPECT_TRUE(VerifyItemListOrdinals()); EXPECT_TRUE(VerifyItemOrder4(2, 0, 3, 1)); // last -> last item_list_.SetItemPosition(item_list_.item_at(3), item_list_.item_at(3)->position().CreateAfter()); EXPECT_TRUE(VerifyItemListOrdinals()); EXPECT_TRUE(VerifyItemOrder4(2, 0, 3, 1)); } } // namespace app_list