folder_image_unittest.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. // Copyright 2014 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. #include "ash/app_list/model/folder_image.h"
  5. #include <string>
  6. #include <utility>
  7. #include "ash/app_list/model/app_list_item.h"
  8. #include "ash/app_list/model/app_list_item_list.h"
  9. #include "ash/app_list/model/app_list_test_model.h"
  10. #include "ash/public/cpp/app_list/app_list_config.h"
  11. #include "ash/public/cpp/app_list/app_list_config_provider.h"
  12. #include "base/test/icu_test_util.h"
  13. #include "testing/gtest/include/gtest/gtest.h"
  14. #include "third_party/skia/include/core/SkBitmap.h"
  15. #include "third_party/skia/include/core/SkColor.h"
  16. #include "ui/gfx/geometry/rect.h"
  17. #include "ui/gfx/skia_util.h"
  18. namespace ash {
  19. namespace {
  20. gfx::ImageSkia CreateSquareBitmapWithColor(int size, SkColor color) {
  21. SkBitmap bitmap;
  22. bitmap.allocN32Pixels(size, size);
  23. bitmap.eraseColor(color);
  24. return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
  25. }
  26. bool ImagesAreEqual(const gfx::ImageSkia& image1,
  27. const gfx::ImageSkia& image2) {
  28. return gfx::BitmapsAreEqual(*image1.bitmap(), *image2.bitmap());
  29. }
  30. // Listens for OnFolderImageUpdated and sets a flag upon receiving the signal.
  31. class TestFolderImageObserver : public FolderImageObserver {
  32. public:
  33. TestFolderImageObserver() : updated_flag_(false) {}
  34. TestFolderImageObserver(const TestFolderImageObserver&) = delete;
  35. TestFolderImageObserver& operator=(const TestFolderImageObserver&) = delete;
  36. bool updated() const { return updated_flag_; }
  37. void Reset() { updated_flag_ = false; }
  38. // FolderImageObserver overrides:
  39. void OnFolderImageUpdated(ash::AppListConfigType config_type) override {
  40. updated_flag_ = true;
  41. }
  42. private:
  43. bool updated_flag_;
  44. };
  45. } // namespace
  46. class FolderImageTest : public testing::Test,
  47. public ::testing::WithParamInterface<
  48. std::tuple<AppListConfigType, bool>> {
  49. public:
  50. FolderImageTest() : scoped_locale_(std::get<1>(GetParam()) ? "he" : "") {}
  51. FolderImageTest(const FolderImageTest&) = delete;
  52. FolderImageTest& operator=(const FolderImageTest&) = delete;
  53. ~FolderImageTest() override = default;
  54. void SetUp() override {
  55. app_list_model_ = std::make_unique<test::AppListTestModel>();
  56. folder_image_ =
  57. std::make_unique<FolderImage>(GetAppListConfig(/*can_create=*/true),
  58. app_list_model_->top_level_item_list());
  59. // Populate the AppListModel with three items (to test that the FolderImage
  60. // correctly supports having fewer than four icons).
  61. AddAppWithColoredIcon("app1", SK_ColorRED);
  62. AddAppWithColoredIcon("app2", SK_ColorGREEN);
  63. AddAppWithColoredIcon("app3", SK_ColorBLUE);
  64. observer_.Reset();
  65. folder_image_->AddObserver(&observer_);
  66. }
  67. void TearDown() override {
  68. folder_image_->RemoveObserver(&observer_);
  69. AppListConfigProvider::Get().ResetForTesting();
  70. }
  71. AppListConfig* GetAppListConfig(bool can_create) {
  72. return AppListConfigProvider::Get().GetConfigForType(
  73. std::get<0>(GetParam()), can_create);
  74. }
  75. bool is_rtl() { return std::get<1>(GetParam()); }
  76. protected:
  77. void AddAppWithColoredIcon(const std::string& id, SkColor icon_color) {
  78. std::unique_ptr<AppListItem> item(new AppListItem(id));
  79. item->SetDefaultIconAndColor(
  80. CreateSquareBitmapWithColor(
  81. SharedAppListConfig::instance().default_grid_icon_dimension(),
  82. icon_color),
  83. IconColor());
  84. static_cast<AppListModel*>(app_list_model_.get())->AddItem(std::move(item));
  85. }
  86. base::test::ScopedRestoreICUDefaultLocale scoped_locale_;
  87. std::unique_ptr<test::AppListTestModel> app_list_model_;
  88. std::unique_ptr<FolderImage> folder_image_;
  89. TestFolderImageObserver observer_;
  90. };
  91. INSTANTIATE_TEST_SUITE_P(
  92. All,
  93. FolderImageTest,
  94. ::testing::Combine(::testing::Values(AppListConfigType::kLarge,
  95. AppListConfigType::kMedium,
  96. AppListConfigType::kSmall),
  97. ::testing::Bool()));
  98. TEST_P(FolderImageTest, UpdateListTest) {
  99. gfx::ImageSkia icon1 = folder_image_->icon();
  100. // Call UpdateIcon and ensure that the observer event fired.
  101. folder_image_->UpdateIcon();
  102. EXPECT_TRUE(observer_.updated());
  103. observer_.Reset();
  104. // The icon should not have changed.
  105. EXPECT_TRUE(ImagesAreEqual(icon1, folder_image_->icon()));
  106. // Swap two items. Ensure that the observer fired and the icon changed.
  107. app_list_model_->top_level_item_list()->MoveItem(2, 1);
  108. EXPECT_TRUE(observer_.updated());
  109. observer_.Reset();
  110. gfx::ImageSkia icon2 = folder_image_->icon();
  111. EXPECT_FALSE(ImagesAreEqual(icon1, icon2));
  112. // Swap back items. Ensure that the observer fired and the icon changed back.
  113. app_list_model_->top_level_item_list()->MoveItem(2, 1);
  114. EXPECT_TRUE(observer_.updated());
  115. observer_.Reset();
  116. EXPECT_TRUE(ImagesAreEqual(icon1, folder_image_->icon()));
  117. // Add a new item. Ensure that the observer fired and the icon changed.
  118. AddAppWithColoredIcon("app4", SK_ColorYELLOW);
  119. EXPECT_TRUE(observer_.updated());
  120. observer_.Reset();
  121. gfx::ImageSkia icon3 = folder_image_->icon();
  122. EXPECT_FALSE(ImagesAreEqual(icon1, icon3));
  123. // Add a new item. The observer should not fire, nor should the icon change
  124. // (because it does not affect the first four icons).
  125. AddAppWithColoredIcon("app5", SK_ColorCYAN);
  126. EXPECT_FALSE(observer_.updated());
  127. observer_.Reset();
  128. EXPECT_TRUE(ImagesAreEqual(icon3, folder_image_->icon()));
  129. // Delete an item. Ensure that the observer fired and the icon changed.
  130. app_list_model_->DeleteItem("app2");
  131. EXPECT_TRUE(observer_.updated());
  132. observer_.Reset();
  133. gfx::ImageSkia icon4 = folder_image_->icon();
  134. EXPECT_FALSE(ImagesAreEqual(icon3, icon4));
  135. }
  136. TEST_P(FolderImageTest, UpdateItemTest) {
  137. gfx::ImageSkia icon1 = folder_image_->icon();
  138. // Change an item's icon. Ensure that the observer fired and the icon changed.
  139. app_list_model_->FindItem("app2")->SetDefaultIconAndColor(
  140. CreateSquareBitmapWithColor(
  141. SharedAppListConfig::instance().default_grid_icon_dimension(),
  142. SK_ColorMAGENTA),
  143. IconColor());
  144. EXPECT_TRUE(observer_.updated());
  145. observer_.Reset();
  146. EXPECT_FALSE(ImagesAreEqual(icon1, folder_image_->icon()));
  147. }
  148. TEST_P(FolderImageTest, GetTargetIconRectInFolderWithSingleItem) {
  149. app_list_model_->DeleteItem("app2");
  150. app_list_model_->DeleteItem("app3");
  151. const AppListConfig* config = GetAppListConfig(/*can_create=*/false);
  152. ASSERT_TRUE(config);
  153. const gfx::Rect test_rects[] = {
  154. gfx::Rect(config->folder_icon_size()),
  155. gfx::Rect(gfx::Point(10, 10), config->folder_icon_size()),
  156. gfx::Rect(config->folder_unclipped_icon_size()),
  157. gfx::Rect(gfx::Point(10, 10), config->folder_unclipped_icon_size()),
  158. };
  159. for (const auto& test_rect : test_rects) {
  160. SCOPED_TRACE(::testing::Message()
  161. << "Target folder icon bounds: " << test_rect.ToString());
  162. const gfx::Point test_rect_center = test_rect.CenterPoint();
  163. const gfx::Size expected_icon_rect_size(
  164. config->item_icon_in_folder_icon_dimension(),
  165. config->item_icon_in_folder_icon_dimension());
  166. gfx::Rect item_1_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  167. *config, app_list_model_->FindItem("app1"), test_rect);
  168. EXPECT_EQ(expected_icon_rect_size, item_1_bounds.size());
  169. EXPECT_EQ(test_rect_center, item_1_bounds.CenterPoint());
  170. }
  171. }
  172. TEST_P(FolderImageTest, GetTargetIconRectInFolderWithTwoItems) {
  173. app_list_model_->DeleteItem("app3");
  174. const AppListConfig* config = GetAppListConfig(/*can_create=*/false);
  175. ASSERT_TRUE(config);
  176. const gfx::Rect test_rects[] = {
  177. gfx::Rect(config->folder_icon_size()),
  178. gfx::Rect(gfx::Point(10, 10), config->folder_icon_size()),
  179. gfx::Rect(config->folder_unclipped_icon_size()),
  180. gfx::Rect(gfx::Point(10, 10), config->folder_unclipped_icon_size()),
  181. };
  182. for (const auto& test_rect : test_rects) {
  183. SCOPED_TRACE(::testing::Message()
  184. << "Target folder icon bounds: " << test_rect.ToString());
  185. const gfx::Point test_rect_center = test_rect.CenterPoint();
  186. const gfx::Size expected_icon_rect_size(
  187. config->item_icon_in_folder_icon_dimension(),
  188. config->item_icon_in_folder_icon_dimension());
  189. gfx::Rect item_1_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  190. *config, app_list_model_->FindItem("app1"), test_rect);
  191. gfx::Rect item_2_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  192. *config, app_list_model_->FindItem("app2"), test_rect);
  193. if (is_rtl())
  194. std::swap(item_1_bounds, item_2_bounds);
  195. EXPECT_EQ(expected_icon_rect_size, item_1_bounds.size());
  196. EXPECT_EQ(
  197. test_rect_center.x() - config->item_icon_in_folder_icon_margin() / 2,
  198. item_1_bounds.right());
  199. EXPECT_EQ(test_rect_center.y(), item_1_bounds.CenterPoint().y());
  200. EXPECT_EQ(expected_icon_rect_size, item_2_bounds.size());
  201. EXPECT_EQ(
  202. test_rect_center.x() + config->item_icon_in_folder_icon_margin() / 2,
  203. item_2_bounds.x());
  204. EXPECT_EQ(test_rect_center.y(), item_2_bounds.CenterPoint().y());
  205. }
  206. }
  207. TEST_P(FolderImageTest, GetTargetIconRectInFolderWithThreeItems) {
  208. const AppListConfig* config = GetAppListConfig(/*can_create=*/false);
  209. ASSERT_TRUE(config);
  210. const gfx::Rect test_rects[] = {
  211. gfx::Rect(config->folder_icon_size()),
  212. gfx::Rect(gfx::Point(10, 10), config->folder_icon_size()),
  213. gfx::Rect(config->folder_unclipped_icon_size()),
  214. gfx::Rect(gfx::Point(10, 10), config->folder_unclipped_icon_size()),
  215. };
  216. for (const auto& test_rect : test_rects) {
  217. SCOPED_TRACE(::testing::Message()
  218. << "Target folder icon bounds: " << test_rect.ToString());
  219. const gfx::Point test_rect_center = test_rect.CenterPoint();
  220. const gfx::Size expected_icon_rect_size(
  221. config->item_icon_in_folder_icon_dimension(),
  222. config->item_icon_in_folder_icon_dimension());
  223. gfx::Rect item_1_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  224. *config, app_list_model_->FindItem("app1"), test_rect);
  225. gfx::Rect item_2_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  226. *config, app_list_model_->FindItem("app2"), test_rect);
  227. gfx::Rect item_3_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  228. *config, app_list_model_->FindItem("app3"), test_rect);
  229. if (is_rtl())
  230. std::swap(item_1_bounds, item_2_bounds);
  231. EXPECT_EQ(expected_icon_rect_size, item_1_bounds.size());
  232. EXPECT_EQ(
  233. test_rect_center.x() - config->item_icon_in_folder_icon_margin() / 2,
  234. item_1_bounds.right());
  235. EXPECT_EQ(
  236. test_rect_center.y() - config->item_icon_in_folder_icon_margin() / 2,
  237. item_1_bounds.bottom());
  238. EXPECT_EQ(expected_icon_rect_size, item_2_bounds.size());
  239. EXPECT_EQ(
  240. test_rect_center.x() + config->item_icon_in_folder_icon_margin() / 2,
  241. item_2_bounds.x());
  242. EXPECT_EQ(
  243. test_rect_center.y() - config->item_icon_in_folder_icon_margin() / 2,
  244. item_2_bounds.bottom());
  245. EXPECT_EQ(expected_icon_rect_size, item_3_bounds.size());
  246. EXPECT_EQ(test_rect_center.x(), item_3_bounds.CenterPoint().x());
  247. EXPECT_EQ(
  248. test_rect_center.y() + config->item_icon_in_folder_icon_margin() / 2,
  249. item_3_bounds.y());
  250. }
  251. }
  252. TEST_P(FolderImageTest, GetTargetIconRectInFolderWithFourItems) {
  253. AddAppWithColoredIcon("app4", SK_ColorYELLOW);
  254. const AppListConfig* config = GetAppListConfig(/*can_create=*/false);
  255. ASSERT_TRUE(config);
  256. const gfx::Rect test_rects[] = {
  257. gfx::Rect(config->folder_icon_size()),
  258. gfx::Rect(gfx::Point(10, 10), config->folder_icon_size()),
  259. gfx::Rect(config->folder_unclipped_icon_size()),
  260. gfx::Rect(gfx::Point(10, 10), config->folder_unclipped_icon_size()),
  261. };
  262. for (const auto& test_rect : test_rects) {
  263. SCOPED_TRACE(::testing::Message()
  264. << "Target folder icon bounds: " << test_rect.ToString());
  265. const gfx::Point test_rect_center = test_rect.CenterPoint();
  266. const gfx::Size expected_icon_rect_size(
  267. config->item_icon_in_folder_icon_dimension(),
  268. config->item_icon_in_folder_icon_dimension());
  269. gfx::Rect item_1_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  270. *config, app_list_model_->FindItem("app1"), test_rect);
  271. gfx::Rect item_2_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  272. *config, app_list_model_->FindItem("app2"), test_rect);
  273. gfx::Rect item_3_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  274. *config, app_list_model_->FindItem("app3"), test_rect);
  275. gfx::Rect item_4_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  276. *config, app_list_model_->FindItem("app4"), test_rect);
  277. if (is_rtl()) {
  278. std::swap(item_1_bounds, item_2_bounds);
  279. std::swap(item_3_bounds, item_4_bounds);
  280. }
  281. EXPECT_EQ(expected_icon_rect_size, item_1_bounds.size());
  282. EXPECT_EQ(
  283. test_rect_center.x() - config->item_icon_in_folder_icon_margin() / 2,
  284. item_1_bounds.right());
  285. EXPECT_EQ(
  286. test_rect_center.y() - config->item_icon_in_folder_icon_margin() / 2,
  287. item_1_bounds.bottom());
  288. EXPECT_EQ(expected_icon_rect_size, item_2_bounds.size());
  289. EXPECT_EQ(
  290. test_rect_center.x() + config->item_icon_in_folder_icon_margin() / 2,
  291. item_2_bounds.x());
  292. EXPECT_EQ(
  293. test_rect_center.y() - config->item_icon_in_folder_icon_margin() / 2,
  294. item_2_bounds.bottom());
  295. EXPECT_EQ(
  296. test_rect_center.x() - config->item_icon_in_folder_icon_margin() / 2,
  297. item_3_bounds.right());
  298. EXPECT_EQ(
  299. test_rect_center.y() + config->item_icon_in_folder_icon_margin() / 2,
  300. item_3_bounds.y());
  301. EXPECT_EQ(expected_icon_rect_size, item_4_bounds.size());
  302. EXPECT_EQ(
  303. test_rect_center.x() + config->item_icon_in_folder_icon_margin() / 2,
  304. item_4_bounds.x());
  305. EXPECT_EQ(
  306. test_rect_center.y() + config->item_icon_in_folder_icon_margin() / 2,
  307. item_4_bounds.y());
  308. }
  309. }
  310. TEST_P(FolderImageTest, GetTargetIconRectInFolderWithFiveItems) {
  311. AddAppWithColoredIcon("app4", SK_ColorYELLOW);
  312. AddAppWithColoredIcon("app5", SK_ColorYELLOW);
  313. const AppListConfig* config = GetAppListConfig(/*can_create=*/false);
  314. ASSERT_TRUE(config);
  315. const gfx::Rect test_rects[] = {
  316. gfx::Rect(config->folder_icon_size()),
  317. gfx::Rect(gfx::Point(10, 10), config->folder_icon_size()),
  318. gfx::Rect(config->folder_unclipped_icon_size()),
  319. gfx::Rect(gfx::Point(10, 10), config->folder_unclipped_icon_size()),
  320. };
  321. for (const auto& test_rect : test_rects) {
  322. SCOPED_TRACE(::testing::Message()
  323. << "Target folder icon bounds: " << test_rect.ToString());
  324. const gfx::Point test_rect_center = test_rect.CenterPoint();
  325. const gfx::Size expected_icon_rect_size(
  326. config->item_icon_in_folder_icon_dimension(),
  327. config->item_icon_in_folder_icon_dimension());
  328. gfx::Rect item_1_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  329. *config, app_list_model_->FindItem("app1"), test_rect);
  330. gfx::Rect item_2_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  331. *config, app_list_model_->FindItem("app2"), test_rect);
  332. gfx::Rect item_3_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  333. *config, app_list_model_->FindItem("app3"), test_rect);
  334. gfx::Rect item_4_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  335. *config, app_list_model_->FindItem("app4"), test_rect);
  336. gfx::Rect item_5_bounds = folder_image_->GetTargetIconRectInFolderForItem(
  337. *config, app_list_model_->FindItem("app5"), test_rect);
  338. if (is_rtl()) {
  339. std::swap(item_1_bounds, item_2_bounds);
  340. std::swap(item_3_bounds, item_4_bounds);
  341. }
  342. EXPECT_EQ(expected_icon_rect_size, item_1_bounds.size());
  343. EXPECT_EQ(
  344. test_rect_center.x() - config->item_icon_in_folder_icon_margin() / 2,
  345. item_1_bounds.right());
  346. EXPECT_EQ(
  347. test_rect_center.y() - config->item_icon_in_folder_icon_margin() / 2,
  348. item_1_bounds.bottom());
  349. EXPECT_EQ(expected_icon_rect_size, item_2_bounds.size());
  350. EXPECT_EQ(
  351. test_rect_center.x() + config->item_icon_in_folder_icon_margin() / 2,
  352. item_2_bounds.x());
  353. EXPECT_EQ(
  354. test_rect_center.y() - config->item_icon_in_folder_icon_margin() / 2,
  355. item_2_bounds.bottom());
  356. EXPECT_EQ(
  357. test_rect_center.x() - config->item_icon_in_folder_icon_margin() / 2,
  358. item_3_bounds.right());
  359. EXPECT_EQ(
  360. test_rect_center.y() + config->item_icon_in_folder_icon_margin() / 2,
  361. item_3_bounds.y());
  362. EXPECT_EQ(expected_icon_rect_size, item_4_bounds.size());
  363. EXPECT_EQ(
  364. test_rect_center.x() + config->item_icon_in_folder_icon_margin() / 2,
  365. item_4_bounds.x());
  366. EXPECT_EQ(
  367. test_rect_center.y() + config->item_icon_in_folder_icon_margin() / 2,
  368. item_4_bounds.y());
  369. EXPECT_EQ(expected_icon_rect_size, item_5_bounds.size());
  370. EXPECT_EQ(test_rect_center, item_5_bounds.CenterPoint());
  371. }
  372. }
  373. } // namespace ash