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.
5 #include "ui/app_list/app_list_model.h"
10 #include "base/command_line.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "ui/app_list/app_list_folder_item.h"
14 #include "ui/app_list/app_list_item.h"
15 #include "ui/app_list/app_list_model_observer.h"
16 #include "ui/app_list/app_list_switches.h"
17 #include "ui/app_list/test/app_list_test_model.h"
18 #include "ui/base/models/list_model_observer.h"
24 class TestObserver
: public AppListModelObserver
{
27 : status_changed_count_(0),
32 virtual ~TestObserver() {
35 // AppListModelObserver
36 virtual void OnAppListModelStatusChanged() OVERRIDE
{
37 ++status_changed_count_
;
40 virtual void OnAppListItemAdded(AppListItem
* item
) OVERRIDE
{
44 virtual void OnAppListItemWillBeDeleted(AppListItem
* item
) OVERRIDE
{
48 virtual void OnAppListItemUpdated(AppListItem
* item
) OVERRIDE
{
52 int status_changed_count() const { return status_changed_count_
; }
53 size_t items_added() { return items_added_
; }
54 size_t items_removed() { return items_removed_
; }
55 size_t items_updated() { return items_updated_
; }
58 status_changed_count_
= 0;
65 int status_changed_count_
;
67 size_t items_removed_
;
68 size_t items_updated_
;
70 DISALLOW_COPY_AND_ASSIGN(TestObserver
);
75 class AppListModelTest
: public testing::Test
{
78 virtual ~AppListModelTest() {}
80 // testing::Test overrides:
81 virtual void SetUp() OVERRIDE
{
82 model_
.AddObserver(&observer_
);
84 virtual void TearDown() OVERRIDE
{
85 model_
.RemoveObserver(&observer_
);
89 bool ItemObservedByFolder(AppListFolderItem
* folder
,
91 return item
->observers_
.HasObserver(folder
);
94 std::string
GetItemListContents(AppListItemList
* item_list
) {
96 for (size_t i
= 0; i
< item_list
->item_count(); ++i
) {
99 s
+= item_list
->item_at(i
)->id();
104 std::string
GetModelContents() {
105 return GetItemListContents(model_
.top_level_item_list());
108 test::AppListTestModel model_
;
109 TestObserver observer_
;
112 DISALLOW_COPY_AND_ASSIGN(AppListModelTest
);
115 TEST_F(AppListModelTest
, SetStatus
) {
116 EXPECT_EQ(AppListModel::STATUS_NORMAL
, model_
.status());
117 model_
.SetStatus(AppListModel::STATUS_SYNCING
);
118 EXPECT_EQ(1, observer_
.status_changed_count());
119 EXPECT_EQ(AppListModel::STATUS_SYNCING
, model_
.status());
120 model_
.SetStatus(AppListModel::STATUS_NORMAL
);
121 EXPECT_EQ(2, observer_
.status_changed_count());
122 // Set the same status, no change is expected.
123 model_
.SetStatus(AppListModel::STATUS_NORMAL
);
124 EXPECT_EQ(2, observer_
.status_changed_count());
127 TEST_F(AppListModelTest
, AppsObserver
) {
128 const size_t num_apps
= 2;
129 model_
.PopulateApps(num_apps
);
130 EXPECT_EQ(num_apps
, observer_
.items_added());
133 TEST_F(AppListModelTest
, ModelGetItem
) {
134 const size_t num_apps
= 2;
135 model_
.PopulateApps(num_apps
);
136 AppListItem
* item0
= model_
.top_level_item_list()->item_at(0);
138 EXPECT_EQ(model_
.GetItemName(0), item0
->id());
139 AppListItem
* item1
= model_
.top_level_item_list()->item_at(1);
141 EXPECT_EQ(model_
.GetItemName(1), item1
->id());
144 TEST_F(AppListModelTest
, ModelFindItem
) {
145 const size_t num_apps
= 2;
146 model_
.PopulateApps(num_apps
);
147 std::string item_name0
= model_
.GetItemName(0);
148 AppListItem
* item0
= model_
.FindItem(item_name0
);
150 EXPECT_EQ(item_name0
, item0
->id());
151 std::string item_name1
= model_
.GetItemName(1);
152 AppListItem
* item1
= model_
.FindItem(item_name1
);
154 EXPECT_EQ(item_name1
, item1
->id());
157 TEST_F(AppListModelTest
, SetItemPosition
) {
158 const size_t num_apps
= 2;
159 model_
.PopulateApps(num_apps
);
160 // Adding another item will add it to the end.
161 model_
.CreateAndAddItem("Added Item 1");
162 ASSERT_EQ(num_apps
+ 1, model_
.top_level_item_list()->item_count());
163 EXPECT_EQ("Added Item 1",
164 model_
.top_level_item_list()->item_at(num_apps
)->id());
165 // Add an item between items 0 and 1.
166 AppListItem
* item0
= model_
.top_level_item_list()->item_at(0);
168 AppListItem
* item1
= model_
.top_level_item_list()->item_at(1);
170 AppListItem
* item2
= model_
.CreateItem("Added Item 2");
171 model_
.AddItem(item2
);
172 EXPECT_EQ("Item 0,Item 1,Added Item 1,Added Item 2", GetModelContents());
173 model_
.SetItemPosition(
174 item2
, item0
->position().CreateBetween(item1
->position()));
175 EXPECT_EQ(num_apps
+ 2, model_
.top_level_item_list()->item_count());
176 EXPECT_EQ(num_apps
+ 2, observer_
.items_added());
177 EXPECT_EQ("Item 0,Added Item 2,Item 1,Added Item 1", GetModelContents());
180 TEST_F(AppListModelTest
, ModelMoveItem
) {
181 const size_t num_apps
= 3;
182 model_
.PopulateApps(num_apps
);
183 // Adding another item will add it to the end.
184 model_
.CreateAndAddItem("Inserted Item");
185 ASSERT_EQ(num_apps
+ 1, model_
.top_level_item_list()->item_count());
186 // Move it to the position 1.
187 observer_
.ResetCounts();
188 model_
.top_level_item_list()->MoveItem(num_apps
, 1);
189 EXPECT_EQ(1u, observer_
.items_updated());
190 EXPECT_EQ("Item 0,Inserted Item,Item 1,Item 2", GetModelContents());
193 TEST_F(AppListModelTest
, ModelRemoveItem
) {
194 const size_t num_apps
= 4;
195 model_
.PopulateApps(num_apps
);
196 // Remove an item in the middle.
197 model_
.DeleteItem(model_
.GetItemName(1));
198 EXPECT_EQ(num_apps
- 1, model_
.top_level_item_list()->item_count());
199 EXPECT_EQ(1u, observer_
.items_removed());
200 EXPECT_EQ("Item 0,Item 2,Item 3", GetModelContents());
201 // Remove the first item in the list.
202 model_
.DeleteItem(model_
.GetItemName(0));
203 EXPECT_EQ(num_apps
- 2, model_
.top_level_item_list()->item_count());
204 EXPECT_EQ(2u, observer_
.items_removed());
205 EXPECT_EQ("Item 2,Item 3", GetModelContents());
206 // Remove the last item in the list.
207 model_
.DeleteItem(model_
.GetItemName(num_apps
- 1));
208 EXPECT_EQ(num_apps
- 3, model_
.top_level_item_list()->item_count());
209 EXPECT_EQ(3u, observer_
.items_removed());
210 EXPECT_EQ("Item 2", GetModelContents());
213 TEST_F(AppListModelTest
, AppOrder
) {
214 const size_t num_apps
= 5;
215 model_
.PopulateApps(num_apps
);
216 // Ensure order is preserved.
217 for (size_t i
= 1; i
< num_apps
; ++i
) {
219 model_
.top_level_item_list()->item_at(i
)->position().GreaterThan(
220 model_
.top_level_item_list()->item_at(i
- 1)->position()));
223 model_
.top_level_item_list()->MoveItem(num_apps
- 1, 1);
224 // Ensure order is preserved.
225 for (size_t i
= 1; i
< num_apps
; ++i
) {
227 model_
.top_level_item_list()->item_at(i
)->position().GreaterThan(
228 model_
.top_level_item_list()->item_at(i
- 1)->position()));
232 class AppListModelFolderTest
: public AppListModelTest
{
234 AppListModelFolderTest() {}
235 virtual ~AppListModelFolderTest() {}
237 // testing::Test overrides:
238 virtual void SetUp() OVERRIDE
{
239 AppListModelTest::SetUp();
241 virtual void TearDown() OVERRIDE
{
242 AppListModelTest::TearDown();
246 DISALLOW_COPY_AND_ASSIGN(AppListModelFolderTest
);
249 TEST_F(AppListModelFolderTest
, FolderItem
) {
250 AppListFolderItem
* folder
=
251 new AppListFolderItem("folder1", AppListFolderItem::FOLDER_TYPE_NORMAL
);
252 const size_t num_folder_apps
= 8;
253 const size_t num_observed_apps
= 4;
254 model_
.AddItem(folder
);
255 for (int i
= 0; static_cast<size_t>(i
) < num_folder_apps
; ++i
) {
256 std::string name
= model_
.GetItemName(i
);
257 model_
.AddItemToFolder(model_
.CreateItem(name
), folder
->id());
259 ASSERT_EQ(num_folder_apps
, folder
->item_list()->item_count());
260 // Check that items 0 and 3 are observed.
261 EXPECT_TRUE(ItemObservedByFolder(
262 folder
, folder
->item_list()->item_at(0)));
263 EXPECT_TRUE(ItemObservedByFolder(
264 folder
, folder
->item_list()->item_at(num_observed_apps
- 1)));
265 // Check that item 4 is not observed.
266 EXPECT_FALSE(ItemObservedByFolder(
267 folder
, folder
->item_list()->item_at(num_observed_apps
)));
268 folder
->item_list()->MoveItem(num_observed_apps
, 0);
269 // Confirm that everything was moved where expected.
270 EXPECT_EQ(model_
.GetItemName(num_observed_apps
),
271 folder
->item_list()->item_at(0)->id());
272 EXPECT_EQ(model_
.GetItemName(0),
273 folder
->item_list()->item_at(1)->id());
274 EXPECT_EQ(model_
.GetItemName(num_observed_apps
- 1),
275 folder
->item_list()->item_at(num_observed_apps
)->id());
276 // Check that items 0 and 3 are observed.
277 EXPECT_TRUE(ItemObservedByFolder(
278 folder
, folder
->item_list()->item_at(0)));
279 EXPECT_TRUE(ItemObservedByFolder(
280 folder
, folder
->item_list()->item_at(num_observed_apps
- 1)));
281 // Check that item 4 is not observed.
282 EXPECT_FALSE(ItemObservedByFolder(
283 folder
, folder
->item_list()->item_at(num_observed_apps
)));
286 TEST_F(AppListModelFolderTest
, MergeItems
) {
287 model_
.PopulateApps(3);
288 ASSERT_EQ(3u, model_
.top_level_item_list()->item_count());
289 AppListItem
* item0
= model_
.top_level_item_list()->item_at(0);
290 AppListItem
* item1
= model_
.top_level_item_list()->item_at(1);
291 AppListItem
* item2
= model_
.top_level_item_list()->item_at(2);
294 std::string folder1_id
= model_
.MergeItems(item0
->id(), item1
->id());
295 ASSERT_EQ(2u, model_
.top_level_item_list()->item_count()); // Folder + 1 item
296 AppListFolderItem
* folder1_item
= model_
.FindFolderItem(folder1_id
);
297 ASSERT_TRUE(folder1_item
);
298 EXPECT_EQ("Item 0,Item 1", GetItemListContents(folder1_item
->item_list()));
300 // Merge an item from the new folder into the third item.
301 std::string folder2_id
= model_
.MergeItems(item2
->id(), item1
->id());
302 ASSERT_EQ(2u, model_
.top_level_item_list()->item_count()); // 2 folders
303 AppListFolderItem
* folder2_item
= model_
.FindFolderItem(folder2_id
);
304 EXPECT_EQ("Item 0", GetItemListContents(folder1_item
->item_list()));
305 EXPECT_EQ("Item 2,Item 1", GetItemListContents(folder2_item
->item_list()));
307 // Merge the remaining item to the new folder, ensure it is added to the end.
308 std::string folder_id
= model_
.MergeItems(folder2_id
, item0
->id());
309 EXPECT_EQ(folder2_id
, folder_id
);
310 EXPECT_EQ("Item 2,Item 1,Item 0",
311 GetItemListContents(folder2_item
->item_list()));
313 // The empty folder should be deleted.
314 folder1_item
= model_
.FindFolderItem(folder1_id
);
315 EXPECT_FALSE(folder1_item
);
318 TEST_F(AppListModelFolderTest
, AddItemToFolder
) {
319 AppListFolderItem
* folder
=
320 new AppListFolderItem("folder1", AppListFolderItem::FOLDER_TYPE_NORMAL
);
321 model_
.AddItem(folder
);
322 AppListItem
* item0
= new AppListItem("Item 0");
323 model_
.AddItemToFolder(item0
, folder
->id());
324 ASSERT_EQ(1u, model_
.top_level_item_list()->item_count());
325 AppListFolderItem
* folder_item
= model_
.FindFolderItem(folder
->id());
326 ASSERT_TRUE(folder_item
);
327 ASSERT_EQ(1u, folder_item
->item_list()->item_count());
328 EXPECT_EQ(item0
, folder_item
->item_list()->item_at(0));
329 EXPECT_EQ(folder
->id(), item0
->folder_id());
332 TEST_F(AppListModelFolderTest
, MoveItemToFolder
) {
333 AppListFolderItem
* folder
=
334 new AppListFolderItem("folder1", AppListFolderItem::FOLDER_TYPE_NORMAL
);
335 model_
.AddItem(folder
);
336 AppListItem
* item0
= new AppListItem("Item 0");
337 AppListItem
* item1
= new AppListItem("Item 1");
338 model_
.AddItem(item0
);
339 model_
.AddItem(item1
);
340 ASSERT_EQ(3u, model_
.top_level_item_list()->item_count());
341 // Move item0 and item1 to folder.
342 std::string folder_id
= folder
->id();
343 model_
.MoveItemToFolder(item0
, folder_id
);
344 model_
.MoveItemToFolder(item1
, folder_id
);
345 AppListFolderItem
* folder_item
= model_
.FindFolderItem(folder_id
);
346 ASSERT_TRUE(folder_item
);
347 EXPECT_EQ(folder_id
, item0
->folder_id());
348 EXPECT_EQ(folder_id
, item1
->folder_id());
349 EXPECT_EQ("Item 0,Item 1", GetItemListContents(folder_item
->item_list()));
350 // Move item0 out of folder.
351 model_
.MoveItemToFolder(item0
, "");
352 EXPECT_EQ("", item0
->folder_id());
353 folder_item
= model_
.FindFolderItem(folder_id
);
354 ASSERT_TRUE(folder_item
);
355 // Move item1 out of folder, folder should be deleted.
356 model_
.MoveItemToFolder(item1
, "");
357 EXPECT_EQ("", item1
->folder_id());
358 folder_item
= model_
.FindFolderItem(folder_id
);
359 EXPECT_FALSE(folder_item
);
362 TEST_F(AppListModelFolderTest
, MoveItemToFolderAt
) {
363 model_
.AddItem(new AppListItem("Item 0"));
364 model_
.AddItem(new AppListItem("Item 1"));
365 AppListFolderItem
* folder1
= static_cast<AppListFolderItem
*>(model_
.AddItem(
366 new AppListFolderItem("folder1", AppListFolderItem::FOLDER_TYPE_NORMAL
)));
367 model_
.AddItem(new AppListItem("Item 2"));
368 model_
.AddItem(new AppListItem("Item 3"));
369 ASSERT_EQ(5u, model_
.top_level_item_list()->item_count());
370 EXPECT_EQ("Item 0,Item 1,folder1,Item 2,Item 3", GetModelContents());
371 // Move Item 1 to folder1, then Item 2 before Item 1.
372 model_
.MoveItemToFolderAt(model_
.top_level_item_list()->item_at(1),
374 syncer::StringOrdinal());
375 EXPECT_EQ("Item 0,folder1,Item 2,Item 3", GetModelContents());
376 model_
.MoveItemToFolderAt(model_
.top_level_item_list()->item_at(2),
378 folder1
->item_list()->item_at(0)->position());
379 EXPECT_EQ("Item 2,Item 1", GetItemListContents(folder1
->item_list()));
380 EXPECT_EQ("Item 0,folder1,Item 3", GetModelContents());
381 // Move Item 2 out of folder to before folder.
382 model_
.MoveItemToFolderAt(
383 folder1
->item_list()->item_at(0), "", folder1
->position());
384 EXPECT_EQ("Item 0,Item 2,folder1,Item 3", GetModelContents());
385 // Move remaining folder item, (Item 1) out of folder to folder position.
386 ASSERT_EQ(1u, folder1
->item_list()->item_count());
387 model_
.MoveItemToFolderAt(
388 folder1
->item_list()->item_at(0), "", folder1
->position());
389 EXPECT_EQ("Item 0,Item 2,Item 1,Item 3", GetModelContents());
392 TEST_F(AppListModelFolderTest
, MoveItemFromFolderToFolder
) {
393 AppListFolderItem
* folder0
=
394 new AppListFolderItem("folder0", AppListFolderItem::FOLDER_TYPE_NORMAL
);
395 AppListFolderItem
* folder1
=
396 new AppListFolderItem("folder1", AppListFolderItem::FOLDER_TYPE_NORMAL
);
397 model_
.AddItem(folder0
);
398 model_
.AddItem(folder1
);
399 EXPECT_EQ("folder0,folder1", GetModelContents());
400 AppListItem
* item0
= new AppListItem("Item 0");
401 AppListItem
* item1
= new AppListItem("Item 1");
402 model_
.AddItemToFolder(item0
, folder0
->id());
403 model_
.AddItemToFolder(item1
, folder0
->id());
404 EXPECT_EQ(folder0
->id(), item0
->folder_id());
405 EXPECT_EQ(folder0
->id(), item1
->folder_id());
406 EXPECT_EQ("Item 0,Item 1", GetItemListContents(folder0
->item_list()));
408 // Move item0 from folder0 to folder1.
409 model_
.MoveItemToFolder(item0
, folder1
->id());
410 ASSERT_EQ(1u, folder0
->item_list()->item_count());
411 ASSERT_EQ(1u, folder1
->item_list()->item_count());
412 EXPECT_EQ(folder1
->id(), item0
->folder_id());
413 EXPECT_EQ("Item 1", GetItemListContents(folder0
->item_list()));
414 EXPECT_EQ("Item 0", GetItemListContents(folder1
->item_list()));
416 // Move item1 from folder0 to folder1. folder0 should get deleted.
417 model_
.MoveItemToFolder(item1
, folder1
->id());
418 ASSERT_EQ(1u, model_
.top_level_item_list()->item_count());
419 ASSERT_EQ(2u, folder1
->item_list()->item_count());
420 EXPECT_EQ(folder1
->id(), item1
->folder_id());
421 EXPECT_EQ("Item 0,Item 1", GetItemListContents(folder1
->item_list()));
423 // Move item1 to a non-existant folder2 which should get created.
424 model_
.MoveItemToFolder(item1
, "folder2");
425 ASSERT_EQ(2u, model_
.top_level_item_list()->item_count());
426 ASSERT_EQ(1u, folder1
->item_list()->item_count());
427 EXPECT_EQ("folder2", item1
->folder_id());
428 AppListFolderItem
* folder2
= model_
.FindFolderItem("folder2");
429 ASSERT_TRUE(folder2
);
432 TEST_F(AppListModelFolderTest
, FindItemInFolder
) {
433 AppListFolderItem
* folder
=
434 new AppListFolderItem("folder1", AppListFolderItem::FOLDER_TYPE_NORMAL
);
436 model_
.AddItem(folder
);
437 std::string folder_id
= folder
->id();
438 AppListItem
* item0
= new AppListItem("Item 0");
439 model_
.AddItemToFolder(item0
, folder_id
);
440 AppListItem
* found_item
= model_
.FindItem(item0
->id());
441 ASSERT_EQ(item0
, found_item
);
442 EXPECT_EQ(folder_id
, found_item
->folder_id());
445 TEST_F(AppListModelFolderTest
, OemFolder
) {
446 AppListFolderItem
* folder
=
447 new AppListFolderItem("folder1", AppListFolderItem::FOLDER_TYPE_OEM
);
448 model_
.AddItem(folder
);
449 std::string folder_id
= folder
->id();
451 // Should not be able to move to an OEM folder with MergeItems.
452 AppListItem
* item0
= new AppListItem("Item 0");
453 model_
.AddItem(item0
);
454 syncer::StringOrdinal item0_pos
= item0
->position();
455 std::string new_folder
= model_
.MergeItems(folder_id
, item0
->id());
456 EXPECT_EQ("", new_folder
);
457 EXPECT_EQ("", item0
->folder_id());
458 EXPECT_TRUE(item0
->position().Equals(item0_pos
));
460 // Should not be able to move from an OEM folder with MoveItemToFolderAt.
461 AppListItem
* item1
= new AppListItem("Item 1");
462 model_
.AddItemToFolder(item1
, folder_id
);
463 syncer::StringOrdinal item1_pos
= item1
->position();
464 bool move_res
= model_
.MoveItemToFolderAt(item1
, "", syncer::StringOrdinal());
465 EXPECT_FALSE(move_res
);
466 EXPECT_TRUE(item1
->position().Equals(item1_pos
));
469 } // namespace app_list