| // Copyright 2016 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 "components/arc/intent_helper/activity_icon_loader.h" |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/run_loop.h" |
| #include "base/test/task_environment.h" |
| #include "components/arc/intent_helper/adaptive_icon_delegate.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/image/image_skia.h" |
| |
| namespace arc { |
| namespace internal { |
| namespace { |
| |
| void OnIconsReady0( |
| std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> activity_to_icons) { |
| EXPECT_EQ(3U, activity_to_icons->size()); |
| EXPECT_EQ(1U, activity_to_icons->count( |
| ActivityIconLoader::ActivityName("p0", "a0"))); |
| EXPECT_EQ(1U, activity_to_icons->count( |
| ActivityIconLoader::ActivityName("p1", "a1"))); |
| EXPECT_EQ(1U, activity_to_icons->count( |
| ActivityIconLoader::ActivityName("p1", "a0"))); |
| } |
| |
| void OnIconsReady1( |
| std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> activity_to_icons) { |
| EXPECT_EQ(1U, activity_to_icons->size()); |
| EXPECT_EQ(1U, activity_to_icons->count( |
| ActivityIconLoader::ActivityName("p1", "a1"))); |
| } |
| |
| void OnIconsReady2( |
| std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> activity_to_icons) { |
| EXPECT_TRUE(activity_to_icons->empty()); |
| } |
| |
| void OnIconsReady3( |
| std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> activity_to_icons) { |
| EXPECT_EQ(2U, activity_to_icons->size()); |
| EXPECT_EQ(1U, activity_to_icons->count( |
| ActivityIconLoader::ActivityName("p1", "a1"))); |
| EXPECT_EQ(1U, activity_to_icons->count( |
| ActivityIconLoader::ActivityName("p2", "a2"))); |
| } |
| |
| class FakeAdaptiveIconDelegate : public AdaptiveIconDelegate { |
| public: |
| FakeAdaptiveIconDelegate() = default; |
| ~FakeAdaptiveIconDelegate() override = default; |
| |
| FakeAdaptiveIconDelegate(const FakeAdaptiveIconDelegate&) = delete; |
| FakeAdaptiveIconDelegate& operator=(const FakeAdaptiveIconDelegate&) = delete; |
| |
| void GenerateAdaptiveIcons( |
| const std::vector<arc::mojom::ActivityIconPtr>& icons, |
| AdaptiveIconDelegateCallback callback) override { |
| ++count_; |
| std::vector<gfx::ImageSkia> result; |
| for (const auto& icon : icons) { |
| if (icon && icon->icon_png_data && |
| icon->icon_png_data->is_adaptive_icon && |
| icon->icon_png_data->foreground_icon_png_data) { |
| auto png_data(icon->icon_png_data->foreground_icon_png_data.value()); |
| png_data_.emplace_back(std::string(png_data.begin(), png_data.end())); |
| result.emplace_back(gfx::ImageSkia()); |
| } |
| } |
| std::move(callback).Run(std::move(result)); |
| } |
| |
| std::vector<std::string> png_data() { return png_data_; } |
| int count() { return count_; } |
| |
| private: |
| int count_ = 0; |
| std::vector<std::string> png_data_; |
| }; |
| |
| // Tests if InvalidateIcons properly cleans up the cache. |
| TEST(ActivityIconLoaderTest, TestInvalidateIcons) { |
| ActivityIconLoader loader; |
| loader.AddCacheEntryForTesting(ActivityIconLoader::ActivityName("p0", "a0")); |
| loader.AddCacheEntryForTesting(ActivityIconLoader::ActivityName("p0", "a1")); |
| loader.AddCacheEntryForTesting(ActivityIconLoader::ActivityName("p1", "a0")); |
| EXPECT_EQ(3U, loader.cached_icons_for_testing().size()); |
| |
| loader.InvalidateIcons("p2"); // delete none. |
| EXPECT_EQ(3U, loader.cached_icons_for_testing().size()); |
| |
| loader.InvalidateIcons("p0"); // delete 2 entries. |
| EXPECT_EQ(1U, loader.cached_icons_for_testing().size()); |
| |
| loader.InvalidateIcons("p2"); // delete none. |
| EXPECT_EQ(1U, loader.cached_icons_for_testing().size()); |
| |
| loader.InvalidateIcons("p1"); // delete 1 entry. |
| EXPECT_EQ(0U, loader.cached_icons_for_testing().size()); |
| } |
| |
| // Tests if GetActivityIcons immediately returns cached icons. |
| TEST(ActivityIconLoaderTest, TestGetActivityIcons) { |
| ActivityIconLoader loader; |
| loader.AddCacheEntryForTesting(ActivityIconLoader::ActivityName("p0", "a0")); |
| loader.AddCacheEntryForTesting(ActivityIconLoader::ActivityName("p1", "a1")); |
| loader.AddCacheEntryForTesting(ActivityIconLoader::ActivityName("p1", "a0")); |
| |
| // Check that GetActivityIcons() immediately calls OnIconsReady0() with all |
| // locally cached icons. |
| std::vector<ActivityIconLoader::ActivityName> activities; |
| activities.emplace_back("p0", "a0"); |
| activities.emplace_back("p1", "a1"); |
| activities.emplace_back("p1", "a0"); |
| EXPECT_EQ( |
| ActivityIconLoader::GetResult::SUCCEEDED_SYNC, |
| loader.GetActivityIcons(activities, base::BindOnce(&OnIconsReady0))); |
| |
| // Test with different |activities|. |
| activities.clear(); |
| activities.emplace_back("p1", "a1"); |
| EXPECT_EQ( |
| ActivityIconLoader::GetResult::SUCCEEDED_SYNC, |
| loader.GetActivityIcons(activities, base::BindOnce(&OnIconsReady1))); |
| activities.clear(); |
| EXPECT_EQ( |
| ActivityIconLoader::GetResult::SUCCEEDED_SYNC, |
| loader.GetActivityIcons(activities, base::BindOnce(&OnIconsReady2))); |
| activities.emplace_back("p1", "a_unknown"); |
| EXPECT_EQ( |
| ActivityIconLoader::GetResult::FAILED_ARC_NOT_SUPPORTED, |
| loader.GetActivityIcons(activities, base::BindOnce(&OnIconsReady2))); |
| } |
| |
| // Tests if OnIconsResized updates the cache. |
| TEST(ActivityIconLoaderTest, TestOnIconsResized) { |
| std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> activity_to_icons( |
| new ActivityIconLoader::ActivityToIconsMap); |
| activity_to_icons->insert(std::make_pair( |
| ActivityIconLoader::ActivityName("p0", "a0"), |
| ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr))); |
| activity_to_icons->insert(std::make_pair( |
| ActivityIconLoader::ActivityName("p1", "a1"), |
| ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr))); |
| activity_to_icons->insert(std::make_pair( |
| ActivityIconLoader::ActivityName("p1", "a0"), |
| ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr))); |
| // Duplicated entey which should be ignored. |
| activity_to_icons->insert(std::make_pair( |
| ActivityIconLoader::ActivityName("p0", "a0"), |
| ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr))); |
| |
| ActivityIconLoader loader; |
| |
| // Call OnIconsResized() and check that the cache is properly updated. |
| loader.OnIconsResizedForTesting(base::BindOnce(&OnIconsReady0), |
| std::move(activity_to_icons)); |
| EXPECT_EQ(3U, loader.cached_icons_for_testing().size()); |
| EXPECT_EQ(1U, loader.cached_icons_for_testing().count( |
| ActivityIconLoader::ActivityName("p0", "a0"))); |
| EXPECT_EQ(1U, loader.cached_icons_for_testing().count( |
| ActivityIconLoader::ActivityName("p1", "a1"))); |
| EXPECT_EQ(1U, loader.cached_icons_for_testing().count( |
| ActivityIconLoader::ActivityName("p1", "a0"))); |
| |
| // Call OnIconsResized() again to make sure that the second call does not |
| // remove the cache the previous call added. |
| activity_to_icons = |
| std::make_unique<ActivityIconLoader::ActivityToIconsMap>(); |
| // Duplicated entry. |
| activity_to_icons->insert(std::make_pair( |
| ActivityIconLoader::ActivityName("p1", "a1"), |
| ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr))); |
| // New entry. |
| activity_to_icons->insert(std::make_pair( |
| ActivityIconLoader::ActivityName("p2", "a2"), |
| ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr))); |
| loader.OnIconsResizedForTesting(base::BindOnce(&OnIconsReady3), |
| std::move(activity_to_icons)); |
| EXPECT_EQ(4U, loader.cached_icons_for_testing().size()); |
| EXPECT_EQ(1U, loader.cached_icons_for_testing().count( |
| ActivityIconLoader::ActivityName("p0", "a0"))); |
| EXPECT_EQ(1U, loader.cached_icons_for_testing().count( |
| ActivityIconLoader::ActivityName("p1", "a1"))); |
| EXPECT_EQ(1U, loader.cached_icons_for_testing().count( |
| ActivityIconLoader::ActivityName("p1", "a0"))); |
| EXPECT_EQ(1U, loader.cached_icons_for_testing().count( |
| ActivityIconLoader::ActivityName("p2", "a2"))); |
| } |
| |
| class ActivityIconLoaderOnIconsReadyTest : public ::testing::Test { |
| public: |
| ActivityIconLoaderOnIconsReadyTest() = default; |
| ~ActivityIconLoaderOnIconsReadyTest() override = default; |
| |
| ActivityIconLoaderOnIconsReadyTest( |
| const ActivityIconLoaderOnIconsReadyTest&) = delete; |
| ActivityIconLoaderOnIconsReadyTest& operator=( |
| const ActivityIconLoaderOnIconsReadyTest&) = delete; |
| |
| void OnIconsReady(std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> |
| activity_to_icons) { |
| EXPECT_EQ(4U, activity_to_icons->size()); |
| EXPECT_EQ(1U, activity_to_icons->count( |
| ActivityIconLoader::ActivityName("p0", "a0"))); |
| EXPECT_EQ(1U, activity_to_icons->count( |
| ActivityIconLoader::ActivityName("p1", "a1"))); |
| EXPECT_EQ(1U, activity_to_icons->count( |
| ActivityIconLoader::ActivityName("p2", "a2"))); |
| EXPECT_EQ(1U, activity_to_icons->count( |
| ActivityIconLoader::ActivityName("p3", "a3"))); |
| if (!on_icon_ready_callback_.is_null()) |
| std::move(on_icon_ready_callback_).Run(); |
| } |
| |
| void WaitForIconReady() { |
| base::RunLoop run_loop; |
| on_icon_ready_callback_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| |
| base::OnceClosure on_icon_ready_callback_; |
| base::test::TaskEnvironment task_environment_; |
| |
| base::WeakPtrFactory<ActivityIconLoaderOnIconsReadyTest> weak_ptr_factory_{ |
| this}; |
| }; |
| |
| // Tests OnIconsReady with a delegate. |
| TEST_F(ActivityIconLoaderOnIconsReadyTest, TestWithDelegate) { |
| ActivityIconLoader loader; |
| std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> activity_to_icons( |
| new ActivityIconLoader::ActivityToIconsMap); |
| auto activity_name0 = ActivityIconLoader::ActivityName("p0", "a0"); |
| auto activity_name1 = ActivityIconLoader::ActivityName("p1", "a1"); |
| loader.AddCacheEntryForTesting(activity_name0); |
| loader.AddCacheEntryForTesting(activity_name1); |
| activity_to_icons->insert(std::make_pair( |
| activity_name0, |
| ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr))); |
| activity_to_icons->insert(std::make_pair( |
| activity_name1, |
| ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr))); |
| |
| std::vector<mojom::ActivityIconPtr> icons; |
| std::string foreground_png_data_as_string0 = "FOREGROUND_ICON_CONTENT_0"; |
| std::string foreground_png_data_as_string1 = "FOREGROUND_ICON_CONTENT_1"; |
| icons.emplace_back(mojom::ActivityIcon::New( |
| mojom::ActivityName::New("p2", "a2"), 32, 32, std::vector<uint8_t>(), |
| mojom::RawIconPngData::New( |
| true, std::vector<uint8_t>(), |
| std::vector<uint8_t>(foreground_png_data_as_string0.begin(), |
| foreground_png_data_as_string0.end()), |
| std::vector<uint8_t>()))); |
| icons.emplace_back(mojom::ActivityIcon::New( |
| mojom::ActivityName::New("p3", "a3"), 32, 32, std::vector<uint8_t>(), |
| mojom::RawIconPngData::New( |
| true, std::vector<uint8_t>(), |
| std::vector<uint8_t>(foreground_png_data_as_string1.begin(), |
| foreground_png_data_as_string1.end()), |
| std::vector<uint8_t>()))); |
| |
| FakeAdaptiveIconDelegate delegate; |
| loader.SetAdaptiveIconDelegate(&delegate); |
| |
| // Call OnIconsReady() and check that the cache is properly updated. |
| loader.OnIconsReadyForTesting( |
| std::move(activity_to_icons), |
| base::BindOnce(&ActivityIconLoaderOnIconsReadyTest::OnIconsReady, |
| weak_ptr_factory_.GetWeakPtr()), |
| std::move(icons)); |
| |
| EXPECT_EQ(1, delegate.count()); |
| EXPECT_EQ(2U, delegate.png_data().size()); |
| EXPECT_EQ(foreground_png_data_as_string0, delegate.png_data()[0]); |
| EXPECT_EQ(foreground_png_data_as_string1, delegate.png_data()[1]); |
| WaitForIconReady(); |
| } |
| |
| // Tests OnIconsReady without a delegate. |
| TEST_F(ActivityIconLoaderOnIconsReadyTest, TestWithoutDelegate) { |
| ActivityIconLoader loader; |
| std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> activity_to_icons( |
| new ActivityIconLoader::ActivityToIconsMap); |
| auto activity_name0 = ActivityIconLoader::ActivityName("p0", "a0"); |
| auto activity_name1 = ActivityIconLoader::ActivityName("p1", "a1"); |
| loader.AddCacheEntryForTesting(activity_name0); |
| loader.AddCacheEntryForTesting(activity_name1); |
| activity_to_icons->insert(std::make_pair( |
| activity_name0, |
| ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr))); |
| activity_to_icons->insert(std::make_pair( |
| activity_name1, |
| ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr))); |
| |
| std::vector<mojom::ActivityIconPtr> icons; |
| icons.emplace_back(mojom::ActivityIcon::New( |
| mojom::ActivityName::New("p2", "a2"), 1, 1, std::vector<uint8_t>(4), |
| mojom::RawIconPngData::New())); |
| icons.emplace_back(mojom::ActivityIcon::New( |
| mojom::ActivityName::New("p3", "a3"), 1, 1, std::vector<uint8_t>(4), |
| mojom::RawIconPngData::New())); |
| |
| // Call OnIconsReady() and check that the cache is properly updated. |
| loader.OnIconsReadyForTesting( |
| std::move(activity_to_icons), |
| base::BindOnce(&ActivityIconLoaderOnIconsReadyTest::OnIconsReady, |
| weak_ptr_factory_.GetWeakPtr()), |
| std::move(icons)); |
| |
| WaitForIconReady(); |
| } |
| |
| } // namespace |
| } // namespace internal |
| } // namespace arc |