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_moved() { return items_moved_; }
58 status_changed_count_ = 0;
65 int status_changed_count_;
67 size_t items_removed_;
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_.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_.item_list()->item_at(0);
138 EXPECT_EQ(model_.GetItemName(0), item0->id());
139 AppListItem* item1 = model_.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_.item_list()->item_count());
163 EXPECT_EQ("Added Item 1", model_.item_list()->item_at(num_apps)->id());
164 // Add an item between items 0 and 1.
165 AppListItem* item0 = model_.item_list()->item_at(0);
167 AppListItem* item1 = model_.item_list()->item_at(1);
169 AppListItem* item2 = model_.CreateItem("Added Item 2", "Added Item 2");
170 model_.AddItem(item2);
171 EXPECT_EQ("Item 0,Item 1,Added Item 1,Added Item 2", GetModelContents());
172 model_.SetItemPosition(
173 item2, item0->position().CreateBetween(item1->position()));
174 EXPECT_EQ(num_apps + 2, model_.item_list()->item_count());
175 EXPECT_EQ(num_apps + 2, observer_.items_added());
176 EXPECT_EQ("Item 0,Added Item 2,Item 1,Added Item 1", GetModelContents());
179 TEST_F(AppListModelTest, ModelMoveItem) {
180 const size_t num_apps = 3;
181 model_.PopulateApps(num_apps);
182 // Adding another item will add it to the end.
183 model_.CreateAndAddItem("Inserted Item");
184 ASSERT_EQ(num_apps + 1, model_.item_list()->item_count());
185 // Move it to the position 1.
186 model_.item_list()->MoveItem(num_apps, 1);
187 EXPECT_EQ(1u, observer_.items_moved());
188 EXPECT_EQ("Item 0,Inserted Item,Item 1,Item 2", GetModelContents());
191 TEST_F(AppListModelTest, ModelRemoveItem) {
192 const size_t num_apps = 4;
193 model_.PopulateApps(num_apps);
194 // Remove an item in the middle.
195 model_.DeleteItem(model_.GetItemName(1));
196 EXPECT_EQ(num_apps - 1, model_.item_list()->item_count());
197 EXPECT_EQ(1u, observer_.items_removed());
198 EXPECT_EQ("Item 0,Item 2,Item 3", GetModelContents());
199 // Remove the first item in the list.
200 model_.DeleteItem(model_.GetItemName(0));
201 EXPECT_EQ(num_apps - 2, model_.item_list()->item_count());
202 EXPECT_EQ(2u, observer_.items_removed());
203 EXPECT_EQ("Item 2,Item 3", GetModelContents());
204 // Remove the last item in the list.
205 model_.DeleteItem(model_.GetItemName(num_apps - 1));
206 EXPECT_EQ(num_apps - 3, model_.item_list()->item_count());
207 EXPECT_EQ(3u, observer_.items_removed());
208 EXPECT_EQ("Item 2", GetModelContents());
211 TEST_F(AppListModelTest, AppOrder) {
212 const size_t num_apps = 5;
213 model_.PopulateApps(num_apps);
214 // Ensure order is preserved.
215 for (size_t i = 1; i < num_apps; ++i) {
216 EXPECT_TRUE(model_.item_list()->item_at(i)->position().GreaterThan(
217 model_.item_list()->item_at(i - 1)->position()));
220 model_.item_list()->MoveItem(num_apps - 1, 1);
221 // Ensure order is preserved.
222 for (size_t i = 1; i < num_apps; ++i) {
223 EXPECT_TRUE(model_.item_list()->item_at(i)->position().GreaterThan(
224 model_.item_list()->item_at(i - 1)->position()));
228 class AppListModelFolderTest : public AppListModelTest {
230 AppListModelFolderTest() {}
231 virtual ~AppListModelFolderTest() {}
233 // testing::Test overrides:
234 virtual void SetUp() OVERRIDE {
235 CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnableFolderUI);
236 AppListModelTest::SetUp();
238 virtual void TearDown() OVERRIDE {
239 AppListModelTest::TearDown();
243 DISALLOW_COPY_AND_ASSIGN(AppListModelFolderTest);
246 TEST_F(AppListModelFolderTest, FolderItem) {
247 AppListFolderItem* folder = new AppListFolderItem("folder1");
248 const size_t num_folder_apps = 8;
249 const size_t num_observed_apps = 4;
250 model_.AddItem(folder);
251 for (int i = 0; static_cast<size_t>(i) < num_folder_apps; ++i) {
252 std::string name = model_.GetItemName(i);
253 model_.AddItemToFolder(model_.CreateItem(name, name), folder->id());
255 ASSERT_EQ(num_folder_apps, folder->item_list()->item_count());
256 // Check that items 0 and 3 are observed.
257 EXPECT_TRUE(ItemObservedByFolder(
258 folder, folder->item_list()->item_at(0)));
259 EXPECT_TRUE(ItemObservedByFolder(
260 folder, folder->item_list()->item_at(num_observed_apps - 1)));
261 // Check that item 4 is not observed.
262 EXPECT_FALSE(ItemObservedByFolder(
263 folder, folder->item_list()->item_at(num_observed_apps)));
264 folder->item_list()->MoveItem(num_observed_apps, 0);
265 // Confirm that everything was moved where expected.
266 EXPECT_EQ(model_.GetItemName(num_observed_apps),
267 folder->item_list()->item_at(0)->id());
268 EXPECT_EQ(model_.GetItemName(0),
269 folder->item_list()->item_at(1)->id());
270 EXPECT_EQ(model_.GetItemName(num_observed_apps - 1),
271 folder->item_list()->item_at(num_observed_apps)->id());
272 // Check that items 0 and 3 are observed.
273 EXPECT_TRUE(ItemObservedByFolder(
274 folder, folder->item_list()->item_at(0)));
275 EXPECT_TRUE(ItemObservedByFolder(
276 folder, folder->item_list()->item_at(num_observed_apps - 1)));
277 // Check that item 4 is not observed.
278 EXPECT_FALSE(ItemObservedByFolder(
279 folder, folder->item_list()->item_at(num_observed_apps)));
282 TEST_F(AppListModelFolderTest, MergeItems) {
283 model_.PopulateApps(3);
284 ASSERT_EQ(3u, model_.item_list()->item_count());
285 AppListItem* item0 = model_.item_list()->item_at(0);
286 AppListItem* item1 = model_.item_list()->item_at(1);
287 AppListItem* item2 = model_.item_list()->item_at(2);
290 std::string folder1_id = model_.MergeItems(item0->id(), item1->id());
291 ASSERT_EQ(2u, model_.item_list()->item_count()); // Folder + 1 item
292 AppListFolderItem* folder1_item = model_.FindFolderItem(folder1_id);
293 ASSERT_TRUE(folder1_item);
294 EXPECT_EQ("Item 0,Item 1", GetItemListContents(folder1_item->item_list()));
296 // Merge an item from the new folder into the third item.
297 std::string folder2_id = model_.MergeItems(item2->id(), item1->id());
298 ASSERT_EQ(2u, model_.item_list()->item_count()); // 2 folders
299 AppListFolderItem* folder2_item = model_.FindFolderItem(folder2_id);
300 EXPECT_EQ("Item 0", GetItemListContents(folder1_item->item_list()));
301 EXPECT_EQ("Item 2,Item 1", GetItemListContents(folder2_item->item_list()));
303 // Merge the remaining item to the new folder, ensure it is added to the end.
304 std::string folder_id = model_.MergeItems(folder2_id, item0->id());
305 EXPECT_EQ(folder2_id, folder_id);
306 EXPECT_EQ("Item 2,Item 1,Item 0",
307 GetItemListContents(folder2_item->item_list()));
309 // The empty folder should be deleted.
310 folder1_item = model_.FindFolderItem(folder1_id);
311 EXPECT_FALSE(folder1_item);
314 TEST_F(AppListModelFolderTest, AddItemToFolder) {
315 AppListFolderItem* folder = new AppListFolderItem("folder1");
316 model_.AddItem(folder);
317 AppListItem* item0 = new AppListItem("Item 0");
318 model_.AddItemToFolder(item0, folder->id());
319 ASSERT_EQ(1u, model_.item_list()->item_count());
320 AppListFolderItem* folder_item = model_.FindFolderItem(folder->id());
321 ASSERT_TRUE(folder_item);
322 ASSERT_EQ(1u, folder_item->item_list()->item_count());
323 EXPECT_EQ(item0, folder_item->item_list()->item_at(0));
324 EXPECT_EQ(folder->id(), item0->folder_id());
327 TEST_F(AppListModelFolderTest, MoveItemToFolder) {
328 AppListFolderItem* folder = new AppListFolderItem("folder1");
329 model_.AddItem(folder);
330 AppListItem* item0 = new AppListItem("Item 0");
331 AppListItem* item1 = new AppListItem("Item 1");
332 model_.AddItem(item0);
333 model_.AddItem(item1);
334 ASSERT_EQ(3u, model_.item_list()->item_count());
335 // Move item0 and item1 to folder.
336 std::string folder_id = folder->id();
337 model_.MoveItemToFolder(item0, folder_id);
338 model_.MoveItemToFolder(item1, folder_id);
339 AppListFolderItem* folder_item = model_.FindFolderItem(folder_id);
340 ASSERT_TRUE(folder_item);
341 EXPECT_EQ(folder_id, item0->folder_id());
342 EXPECT_EQ(folder_id, item1->folder_id());
343 EXPECT_EQ("Item 0,Item 1", GetItemListContents(folder_item->item_list()));
344 // Move item0 out of folder.
345 model_.MoveItemToFolder(item0, "");
346 EXPECT_EQ("", item0->folder_id());
347 folder_item = model_.FindFolderItem(folder_id);
348 ASSERT_TRUE(folder_item);
349 // Move item1 out of folder, folder should be deleted.
350 model_.MoveItemToFolder(item1, "");
351 EXPECT_EQ("", item1->folder_id());
352 folder_item = model_.FindFolderItem(folder_id);
353 EXPECT_FALSE(folder_item);
356 TEST_F(AppListModelFolderTest, MoveItemToFolderAt) {
357 model_.AddItem(new AppListItem("Item 0"));
358 model_.AddItem(new AppListItem("Item 1"));
359 AppListFolderItem* folder1 = static_cast<AppListFolderItem*>(
360 model_.AddItem(new AppListFolderItem("folder1")));
361 model_.AddItem(new AppListItem("Item 2"));
362 model_.AddItem(new AppListItem("Item 3"));
363 ASSERT_EQ(5u, model_.item_list()->item_count());
364 EXPECT_EQ("Item 0,Item 1,folder1,Item 2,Item 3", GetModelContents());
365 // Move Item 1 to folder1, then Item 2 before Item 1.
366 model_.MoveItemToFolderAt(
367 model_.item_list()->item_at(1), folder1->id(), syncer::StringOrdinal());
368 EXPECT_EQ("Item 0,folder1,Item 2,Item 3", GetModelContents());
369 model_.MoveItemToFolderAt(
370 model_.item_list()->item_at(2), folder1->id(),
371 folder1->item_list()->item_at(0)->position());
372 EXPECT_EQ("Item 2,Item 1", GetItemListContents(folder1->item_list()));
373 EXPECT_EQ("Item 0,folder1,Item 3", GetModelContents());
374 // Move Item 2 out of folder to before folder.
375 model_.MoveItemToFolderAt(
376 folder1->item_list()->item_at(0), "", folder1->position());
377 EXPECT_EQ("Item 0,Item 2,folder1,Item 3", GetModelContents());
378 // Move remaining folder item, (Item 1) out of folder to folder position.
379 ASSERT_EQ(1u, folder1->item_list()->item_count());
380 model_.MoveItemToFolderAt(
381 folder1->item_list()->item_at(0), "", folder1->position());
382 EXPECT_EQ("Item 0,Item 2,Item 1,Item 3", GetModelContents());
385 TEST_F(AppListModelFolderTest, MoveItemFromFolderToFolder) {
386 AppListFolderItem* folder0 = new AppListFolderItem("folder0");
387 AppListFolderItem* folder1 = new AppListFolderItem("folder1");
388 model_.AddItem(folder0);
389 model_.AddItem(folder1);
390 EXPECT_EQ("folder0,folder1", GetModelContents());
391 AppListItem* item0 = new AppListItem("Item 0");
392 AppListItem* item1 = new AppListItem("Item 1");
393 model_.AddItemToFolder(item0, folder0->id());
394 model_.AddItemToFolder(item1, folder0->id());
395 EXPECT_EQ(folder0->id(), item0->folder_id());
396 EXPECT_EQ(folder0->id(), item1->folder_id());
397 EXPECT_EQ("Item 0,Item 1", GetItemListContents(folder0->item_list()));
399 // Move item0 from folder0 to folder1.
400 model_.MoveItemToFolder(item0, folder1->id());
401 ASSERT_EQ(1u, folder0->item_list()->item_count());
402 ASSERT_EQ(1u, folder1->item_list()->item_count());
403 EXPECT_EQ(folder1->id(), item0->folder_id());
404 EXPECT_EQ("Item 1", GetItemListContents(folder0->item_list()));
405 EXPECT_EQ("Item 0", GetItemListContents(folder1->item_list()));
407 // Move item1 from folder0 to folder1. folder0 should get deleted.
408 model_.MoveItemToFolder(item1, folder1->id());
409 ASSERT_EQ(1u, model_.item_list()->item_count());
410 ASSERT_EQ(2u, folder1->item_list()->item_count());
411 EXPECT_EQ(folder1->id(), item1->folder_id());
412 EXPECT_EQ("Item 0,Item 1", GetItemListContents(folder1->item_list()));
414 // Move item1 to a non-existant folder2 which should get created.
415 model_.MoveItemToFolder(item1, "folder2");
416 ASSERT_EQ(2u, model_.item_list()->item_count());
417 ASSERT_EQ(1u, folder1->item_list()->item_count());
418 EXPECT_EQ("folder2", item1->folder_id());
419 AppListFolderItem* folder2 = model_.FindFolderItem("folder2");
420 ASSERT_TRUE(folder2);
423 TEST_F(AppListModelFolderTest, FindItemInFolder) {
424 AppListFolderItem* folder = new AppListFolderItem("folder1");
426 model_.AddItem(folder);
427 std::string folder_id = folder->id();
428 AppListItem* item0 = new AppListItem("Item 0");
429 model_.AddItemToFolder(item0, folder_id);
430 AppListItem* found_item = model_.FindItem(item0->id());
431 ASSERT_EQ(item0, found_item);
432 EXPECT_EQ(folder_id, found_item->folder_id());
435 } // namespace app_list