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/app_list/app_list_item_list.h"
6
7#include "base/memory/scoped_ptr.h"
8#include "base/strings/stringprintf.h"
9#include "testing/gtest/include/gtest/gtest.h"
10#include "ui/app_list/app_list_folder_item.h"
11#include "ui/app_list/app_list_item.h"
12#include "ui/app_list/app_list_item_list_observer.h"
13
14namespace app_list {
15
16namespace {
17
18class TestObserver : public AppListItemListObserver {
19 public:
20  TestObserver() : items_added_(0), items_removed_(0), items_moved_(0) {}
21
22  virtual ~TestObserver() {
23  }
24
25  // AppListItemListObserver overriden:
26  virtual void OnListItemAdded(size_t index, AppListItem* item) OVERRIDE {
27    ++items_added_;
28  }
29
30  virtual void OnListItemRemoved(size_t index, AppListItem* item) OVERRIDE {
31    ++items_removed_;
32  }
33
34  virtual void OnListItemMoved(size_t from_index,
35                               size_t to_index,
36                               AppListItem* item) OVERRIDE {
37    ++items_moved_;
38  }
39
40  size_t items_added() const { return items_added_; }
41  size_t items_removed() const { return items_removed_; }
42  size_t items_moved() const { return items_moved_; }
43
44  void ResetCounts() {
45    items_added_ = 0;
46    items_removed_ = 0;
47    items_moved_ = 0;
48  }
49
50 private:
51  size_t items_added_;
52  size_t items_removed_;
53  size_t items_moved_;
54
55  DISALLOW_COPY_AND_ASSIGN(TestObserver);
56};
57
58std::string GetItemId(int id) {
59  return base::StringPrintf("Item %d", id);
60}
61
62}  // namespace
63
64class AppListItemListTest : public testing::Test {
65 public:
66  AppListItemListTest() {}
67  virtual ~AppListItemListTest() {}
68
69  // testing::Test overrides:
70  virtual void SetUp() OVERRIDE {
71    item_list_.AddObserver(&observer_);
72  }
73
74  virtual void TearDown() OVERRIDE {
75    item_list_.RemoveObserver(&observer_);
76  }
77
78 protected:
79  AppListItem* FindItem(const std::string& id) {
80    return item_list_.FindItem(id);
81  }
82
83  bool FindItemIndex(const std::string& id, size_t* index) {
84    return item_list_.FindItemIndex(id, index);
85  }
86
87  scoped_ptr<AppListItem> CreateItem(const std::string& name) {
88    scoped_ptr<AppListItem> item(new AppListItem(name));
89    size_t nitems = item_list_.item_count();
90    syncer::StringOrdinal position;
91    if (nitems == 0)
92      position = syncer::StringOrdinal::CreateInitialOrdinal();
93    else
94      position = item_list_.item_at(nitems - 1)->position().CreateAfter();
95    item->set_position(position);
96    return item.Pass();
97  }
98
99  AppListItem* CreateAndAddItem(const std::string& name) {
100    scoped_ptr<AppListItem> item(CreateItem(name));
101    return item_list_.AddItem(item.Pass());
102  }
103
104  scoped_ptr<AppListItem> RemoveItem(const std::string& id) {
105    return item_list_.RemoveItem(id);
106  }
107
108  scoped_ptr<AppListItem> RemoveItemAt(size_t index) {
109    return item_list_.RemoveItemAt(index);
110  }
111
112  syncer::StringOrdinal CreatePositionBefore(
113      const syncer::StringOrdinal& position) {
114    return item_list_.CreatePositionBefore(position);
115  }
116
117  bool VerifyItemListOrdinals() {
118    bool res = true;
119    for (size_t i = 1; i < item_list_.item_count(); ++i) {
120      res &= (item_list_.item_at(i - 1)->position().LessThan(
121          item_list_.item_at(i)->position()));
122    }
123    if (!res)
124      PrintItems();
125    return res;
126  }
127
128  bool VerifyItemOrder4(size_t a, size_t b, size_t c, size_t d) {
129    if ((GetItemId(a) == item_list_.item_at(0)->id()) &&
130        (GetItemId(b) == item_list_.item_at(1)->id()) &&
131        (GetItemId(c) == item_list_.item_at(2)->id()) &&
132        (GetItemId(d) == item_list_.item_at(3)->id()))
133      return true;
134    PrintItems();
135    return false;
136  }
137
138  void PrintItems() {
139    VLOG(1) << "ITEMS:";
140    for (size_t i = 0; i < item_list_.item_count(); ++i)
141      VLOG(1) << " " << item_list_.item_at(i)->ToDebugString();
142  }
143
144  AppListItemList item_list_;
145  TestObserver observer_;
146
147 private:
148  DISALLOW_COPY_AND_ASSIGN(AppListItemListTest);
149};
150
151TEST_F(AppListItemListTest, FindItemIndex) {
152  AppListItem* item_0 = CreateAndAddItem(GetItemId(0));
153  AppListItem* item_1 = CreateAndAddItem(GetItemId(1));
154  AppListItem* item_2 = CreateAndAddItem(GetItemId(2));
155  EXPECT_EQ(observer_.items_added(), 3u);
156  EXPECT_EQ(item_list_.item_count(), 3u);
157  EXPECT_EQ(item_0, item_list_.item_at(0));
158  EXPECT_EQ(item_1, item_list_.item_at(1));
159  EXPECT_EQ(item_2, item_list_.item_at(2));
160  EXPECT_TRUE(VerifyItemListOrdinals());
161
162  size_t index;
163  EXPECT_TRUE(FindItemIndex(item_0->id(), &index));
164  EXPECT_EQ(index, 0u);
165  EXPECT_TRUE(FindItemIndex(item_1->id(), &index));
166  EXPECT_EQ(index, 1u);
167  EXPECT_TRUE(FindItemIndex(item_2->id(), &index));
168  EXPECT_EQ(index, 2u);
169
170  scoped_ptr<AppListItem> item_3(CreateItem(GetItemId(3)));
171  EXPECT_FALSE(FindItemIndex(item_3->id(), &index));
172}
173
174TEST_F(AppListItemListTest, RemoveItemAt) {
175  AppListItem* item_0 = CreateAndAddItem(GetItemId(0));
176  AppListItem* item_1 = CreateAndAddItem(GetItemId(1));
177  AppListItem* item_2 = CreateAndAddItem(GetItemId(2));
178  EXPECT_EQ(item_list_.item_count(), 3u);
179  EXPECT_EQ(observer_.items_added(), 3u);
180  size_t index;
181  EXPECT_TRUE(FindItemIndex(item_1->id(), &index));
182  EXPECT_EQ(index, 1u);
183  EXPECT_TRUE(VerifyItemListOrdinals());
184
185  scoped_ptr<AppListItem> item_removed = RemoveItemAt(1);
186  EXPECT_EQ(item_removed, item_1);
187  EXPECT_FALSE(FindItem(item_1->id()));
188  EXPECT_EQ(item_list_.item_count(), 2u);
189  EXPECT_EQ(observer_.items_removed(), 1u);
190  EXPECT_EQ(item_list_.item_at(0), item_0);
191  EXPECT_EQ(item_list_.item_at(1), item_2);
192  EXPECT_TRUE(VerifyItemListOrdinals());
193}
194
195TEST_F(AppListItemListTest, RemoveItem) {
196  AppListItem* item_0 = CreateAndAddItem(GetItemId(0));
197  AppListItem* item_1 = CreateAndAddItem(GetItemId(1));
198  AppListItem* item_2 = CreateAndAddItem(GetItemId(2));
199  EXPECT_EQ(item_list_.item_count(), 3u);
200  EXPECT_EQ(observer_.items_added(), 3u);
201  EXPECT_EQ(item_0, item_list_.item_at(0));
202  EXPECT_EQ(item_1, item_list_.item_at(1));
203  EXPECT_EQ(item_2, item_list_.item_at(2));
204  EXPECT_TRUE(VerifyItemListOrdinals());
205
206  size_t index;
207  EXPECT_TRUE(FindItemIndex(item_1->id(), &index));
208  EXPECT_EQ(index, 1u);
209
210  scoped_ptr<AppListItem> item_removed = RemoveItem(item_1->id());
211  EXPECT_EQ(item_removed, item_1);
212  EXPECT_FALSE(FindItem(item_1->id()));
213  EXPECT_EQ(item_list_.item_count(), 2u);
214  EXPECT_EQ(observer_.items_removed(), 1u);
215  EXPECT_TRUE(VerifyItemListOrdinals());
216}
217
218TEST_F(AppListItemListTest, MoveItem) {
219  CreateAndAddItem(GetItemId(0));
220  CreateAndAddItem(GetItemId(1));
221  CreateAndAddItem(GetItemId(2));
222  CreateAndAddItem(GetItemId(3));
223
224  EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3));
225
226  item_list_.MoveItem(0, 0);
227  EXPECT_EQ(0u, observer_.items_moved());
228  EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3));
229
230  item_list_.MoveItem(0, 1);
231  EXPECT_EQ(1u, observer_.items_moved());
232  EXPECT_TRUE(VerifyItemListOrdinals());
233  EXPECT_TRUE(VerifyItemOrder4(1, 0, 2, 3));
234
235  item_list_.MoveItem(1, 2);
236  EXPECT_EQ(2u, observer_.items_moved());
237  EXPECT_TRUE(VerifyItemListOrdinals());
238  EXPECT_TRUE(VerifyItemOrder4(1, 2, 0, 3));
239
240  item_list_.MoveItem(2, 1);
241  EXPECT_EQ(3u, observer_.items_moved());
242  EXPECT_TRUE(VerifyItemListOrdinals());
243  EXPECT_TRUE(VerifyItemOrder4(1, 0, 2, 3));
244
245  item_list_.MoveItem(3, 0);
246  EXPECT_EQ(4u, observer_.items_moved());
247  EXPECT_TRUE(VerifyItemListOrdinals());
248  EXPECT_TRUE(VerifyItemOrder4(3, 1, 0, 2));
249
250  item_list_.MoveItem(0, 3);
251  EXPECT_EQ(5u, observer_.items_moved());
252  EXPECT_TRUE(VerifyItemListOrdinals());
253  EXPECT_TRUE(VerifyItemOrder4(1, 0, 2, 3));
254}
255
256TEST_F(AppListItemListTest, SamePosition) {
257  CreateAndAddItem(GetItemId(0));
258  CreateAndAddItem(GetItemId(1));
259  CreateAndAddItem(GetItemId(2));
260  CreateAndAddItem(GetItemId(3));
261  item_list_.SetItemPosition(item_list_.item_at(2),
262                             item_list_.item_at(1)->position());
263  EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3));
264  EXPECT_TRUE(item_list_.item_at(1)->position().Equals(
265      item_list_.item_at(2)->position()));
266  // Moving an item to position 1 should fix the position.
267  observer_.ResetCounts();
268  item_list_.MoveItem(0, 1);
269  EXPECT_TRUE(VerifyItemOrder4(1, 0, 2, 3));
270  EXPECT_TRUE(item_list_.item_at(0)->position().LessThan(
271      item_list_.item_at(1)->position()));
272  EXPECT_TRUE(item_list_.item_at(1)->position().LessThan(
273      item_list_.item_at(2)->position()));
274  EXPECT_TRUE(item_list_.item_at(2)->position().LessThan(
275      item_list_.item_at(3)->position()));
276  // One extra move notification for fixed position.
277  EXPECT_EQ(2u, observer_.items_moved());
278
279  // Restore the original order.
280  item_list_.MoveItem(1, 0);
281  EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3));
282
283  // Set all four items to the same position.
284  item_list_.SetItemPosition(item_list_.item_at(1),
285                             item_list_.item_at(0)->position());
286  item_list_.SetItemPosition(item_list_.item_at(2),
287                             item_list_.item_at(0)->position());
288  item_list_.SetItemPosition(item_list_.item_at(3),
289                             item_list_.item_at(0)->position());
290  // Move should fix the position of the items.
291  observer_.ResetCounts();
292  item_list_.MoveItem(0, 1);
293  EXPECT_TRUE(VerifyItemOrder4(1, 0, 2, 3));
294  EXPECT_TRUE(item_list_.item_at(0)->position().LessThan(
295      item_list_.item_at(1)->position()));
296  EXPECT_TRUE(item_list_.item_at(1)->position().LessThan(
297      item_list_.item_at(2)->position()));
298  EXPECT_TRUE(item_list_.item_at(2)->position().LessThan(
299      item_list_.item_at(3)->position()));
300  // One extra move notification for fixed position.
301  EXPECT_EQ(2u, observer_.items_moved());
302}
303
304TEST_F(AppListItemListTest, CreatePositionBefore) {
305  CreateAndAddItem(GetItemId(0));
306  syncer::StringOrdinal position0 = item_list_.item_at(0)->position();
307  syncer::StringOrdinal new_position;
308  new_position = CreatePositionBefore(position0.CreateBefore());
309  EXPECT_TRUE(new_position.LessThan(position0));
310  new_position = CreatePositionBefore(position0);
311  EXPECT_TRUE(new_position.LessThan(position0));
312  new_position = CreatePositionBefore(position0.CreateAfter());
313  EXPECT_TRUE(new_position.GreaterThan(position0));
314
315  CreateAndAddItem(GetItemId(1));
316  syncer::StringOrdinal position1 = item_list_.item_at(1)->position();
317  EXPECT_TRUE(position1.GreaterThan(position0));
318  new_position = CreatePositionBefore(position1);
319  EXPECT_TRUE(new_position.GreaterThan(position0));
320  EXPECT_TRUE(new_position.LessThan(position1));
321
322  // Invalid ordinal should return a position at the end of the list.
323  new_position = CreatePositionBefore(syncer::StringOrdinal());
324  EXPECT_TRUE(new_position.GreaterThan(position1));
325}
326
327TEST_F(AppListItemListTest, SetItemPosition) {
328  CreateAndAddItem(GetItemId(0));
329  CreateAndAddItem(GetItemId(1));
330  CreateAndAddItem(GetItemId(2));
331  CreateAndAddItem(GetItemId(3));
332  EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3));
333
334  // No change to position.
335  item_list_.SetItemPosition(item_list_.item_at(0),
336                             item_list_.item_at(0)->position());
337  EXPECT_TRUE(VerifyItemListOrdinals());
338  EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3));
339  // No order change.
340  item_list_.SetItemPosition(item_list_.item_at(0),
341                             item_list_.item_at(0)->position().CreateBetween(
342                                 item_list_.item_at(1)->position()));
343  EXPECT_TRUE(VerifyItemListOrdinals());
344  EXPECT_TRUE(VerifyItemOrder4(0, 1, 2, 3));
345  // 0 -> 1
346  item_list_.SetItemPosition(item_list_.item_at(0),
347                             item_list_.item_at(1)->position().CreateBetween(
348                                 item_list_.item_at(2)->position()));
349  EXPECT_TRUE(VerifyItemListOrdinals());
350  EXPECT_TRUE(VerifyItemOrder4(1, 0, 2, 3));
351  // 1 -> 2
352  item_list_.SetItemPosition(item_list_.item_at(1),
353                             item_list_.item_at(2)->position().CreateBetween(
354                                 item_list_.item_at(3)->position()));
355  EXPECT_TRUE(VerifyItemListOrdinals());
356  EXPECT_TRUE(VerifyItemOrder4(1, 2, 0, 3));
357  // 0 -> last
358  item_list_.SetItemPosition(item_list_.item_at(0),
359                             item_list_.item_at(3)->position().CreateAfter());
360  EXPECT_TRUE(VerifyItemListOrdinals());
361  EXPECT_TRUE(VerifyItemOrder4(2, 0, 3, 1));
362  // last -> last
363  item_list_.SetItemPosition(item_list_.item_at(3),
364                             item_list_.item_at(3)->position().CreateAfter());
365  EXPECT_TRUE(VerifyItemListOrdinals());
366  EXPECT_TRUE(VerifyItemOrder4(2, 0, 3, 1));
367}
368
369}  // namespace app_list
370