Index: chrome/browser/ui/app_list/search/launcher_search/extension_badged_icon_image_unittest.cc |
diff --git a/chrome/browser/ui/app_list/search/launcher_search/extension_badged_icon_image_unittest.cc b/chrome/browser/ui/app_list/search/launcher_search/extension_badged_icon_image_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..46e8f474780a3ca93dc2a007ec23387a5332cc33 |
--- /dev/null |
+++ b/chrome/browser/ui/app_list/search/launcher_search/extension_badged_icon_image_unittest.cc |
@@ -0,0 +1,269 @@ |
+// Copyright 2015 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 "chrome/browser/ui/app_list/search/launcher_search/extension_badged_icon_image.h" |
+ |
+#include "chrome/browser/chromeos/launcher_search_provider/error_reporter.h" |
+#include "extensions/common/manifest_constants.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "ui/gfx/canvas.h" |
+#include "ui/gfx/geometry/rect.h" |
+#include "ui/gfx/geometry/size.h" |
+#include "ui/gfx/image/canvas_image_source.h" |
+#include "ui/gfx/image/image_skia.h" |
+#include "ui/gfx/image/image_unittest_util.h" |
+#include "url/gurl.h" |
+ |
+using chromeos::launcher_search_provider::ErrorReporter; |
+ |
+namespace app_list { |
+ |
+namespace { |
+ |
+const char kTestExtensionId[] = "foo"; |
+const char kTestCustomIconURL[] = "chrome-extension://foo/bar"; |
+ |
+// This image source generates following image: |
+// |
+// ### |
+// #** |
+// #** |
+// |
+// where # is the primary fill color, and * is the secondary fill color. |
+class BadgedImageSource : public gfx::CanvasImageSource { |
+ public: |
+ BadgedImageSource(const gfx::Size& image_size, |
+ const SkColor primary_fill_color) |
+ : CanvasImageSource(image_size, false), |
+ primary_fill_color_(primary_fill_color), |
+ secondary_fill_color_(SK_ColorTRANSPARENT) {} |
+ |
+ BadgedImageSource(const gfx::Size& image_size, |
+ const SkColor primary_fill_color, |
+ const SkColor secondary_fill_color) |
+ : CanvasImageSource(image_size, false), |
+ primary_fill_color_(primary_fill_color), |
+ secondary_fill_color_(secondary_fill_color) {} |
+ |
+ void Draw(gfx::Canvas* canvas) override { |
+ canvas->FillRect(gfx::Rect(size()), primary_fill_color_); |
+ |
+ const int badge_dimension = size().width() * 2 / 3; |
+ const int offset = size().width() - badge_dimension; |
+ canvas->FillRect( |
+ gfx::Rect(offset, offset, badge_dimension, badge_dimension), |
+ secondary_fill_color_); |
+ } |
+ |
+ private: |
+ const SkColor primary_fill_color_; |
+ const SkColor secondary_fill_color_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(BadgedImageSource); |
+}; |
+ |
+// Test implementation of ExtensionBadgedIconImage. |
+class ExtensionBadgedIconImageTestImpl : public ExtensionBadgedIconImage { |
+ public: |
+ // Use base class constructor. |
+ using ExtensionBadgedIconImage::ExtensionBadgedIconImage; |
+ |
+ const gfx::ImageSkia& LoadExtensionIcon() override { |
+ // Returns 32x32 black image. |
+ extension_icon_ = gfx::ImageSkia( |
+ new BadgedImageSource(icon_size_, SK_ColorBLACK), icon_size_); |
+ return extension_icon_; |
+ } |
+ |
+ // Calls OnExtensionIconImageChnaged callback with |extension_icon|. |
+ void LoadExtensionIconAsync(const gfx::ImageSkia& image) { |
+ OnExtensionIconChanged(image); |
+ } |
+ |
+ void LoadIconResourceFromExtension() override { |
+ // For success case, returns 32x32 blue image. |
+ is_load_extension_icon_resource_called_ = true; |
+ } |
+ |
+ bool IsLoadExtensionIconResourceCalled() const { |
+ return is_load_extension_icon_resource_called_; |
+ } |
+ |
+ // Calls OnCustomIconLoaded callback with |custom_icon|. Sets an empty image |
+ // for simulating a failure case. |
+ void CallOnCustomIconLoaded(gfx::ImageSkia custom_icon) { |
+ OnCustomIconLoaded(custom_icon); |
+ } |
+ |
+ private: |
+ gfx::ImageSkia extension_icon_; |
+ bool is_load_extension_icon_resource_called_ = false; |
+}; |
+ |
+// A fake error reporter to test error message. |
+class FakeErrorReporter : public ErrorReporter { |
+ public: |
+ FakeErrorReporter() : ErrorReporter(nullptr, -1) { |
+ last_message_.reset(new std::string()); |
+ } |
+ explicit FakeErrorReporter(const linked_ptr<std::string>& last_message) |
+ : ErrorReporter(nullptr, -1), last_message_(last_message) {} |
+ ~FakeErrorReporter() override {} |
+ void Warn(const std::string& message) override { |
+ last_message_->clear(); |
+ last_message_->append(message); |
+ } |
+ |
+ const std::string& GetLastWarningMessage() { return *last_message_.get(); } |
+ |
+ scoped_ptr<ErrorReporter> Duplicate() override { |
+ return make_scoped_ptr(new FakeErrorReporter(last_message_)); |
+ } |
+ |
+ private: |
+ linked_ptr<std::string> last_message_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(FakeErrorReporter); |
+}; |
+ |
+// Creates a test extension with |extension_id|. |
+scoped_refptr<extensions::Extension> CreateTestExtension( |
+ const std::string& extension_id) { |
+ base::DictionaryValue manifest; |
+ std::string error; |
+ manifest.SetStringWithoutPathExpansion(extensions::manifest_keys::kVersion, |
+ "1"); |
+ manifest.SetStringWithoutPathExpansion(extensions::manifest_keys::kName, |
+ "TestExtension"); |
+ return extensions::Extension::Create( |
+ base::FilePath(), extensions::Manifest::UNPACKED, manifest, |
+ extensions::Extension::NO_FLAGS, extension_id, &error); |
+} |
+ |
+// Returns true if icon image of |badged_icon_image| equals to |expected_image|. |
+bool IsEqual(const gfx::ImageSkia& expected_image, |
+ const ExtensionBadgedIconImage& badged_icon_image) { |
+ return gfx::test::IsEqual( |
+ expected_image.GetRepresentation(1.0).sk_bitmap(), |
+ badged_icon_image.GetIconImage().GetRepresentation(1.0).sk_bitmap()); |
+} |
+ |
+} // namespace |
+ |
+class ExtensionBadgedIconImageTest : public testing::Test { |
+ protected: |
+ void SetUp() override { extension_ = CreateTestExtension(kTestExtensionId); } |
+ |
+ scoped_ptr<FakeErrorReporter> GetFakeErrorReporter() { |
+ return make_scoped_ptr(new FakeErrorReporter()); |
+ } |
+ |
+ scoped_refptr<extensions::Extension> extension_; |
+}; |
+ |
+TEST_F(ExtensionBadgedIconImageTest, WithoutCustomIconSuccessCase) { |
+ GURL icon_url; // No custom icon. |
+ ExtensionBadgedIconImageTestImpl impl(icon_url, nullptr, nullptr, 32, |
+ GetFakeErrorReporter()); |
+ impl.LoadResources(); |
+ |
+ // Icon should be black image. |
+ gfx::Size icon_size(32, 32); |
+ gfx::ImageSkia expected_image(new BadgedImageSource(icon_size, SK_ColorBLACK), |
+ icon_size); |
+ ASSERT_TRUE(IsEqual(expected_image, impl)); |
+} |
+ |
+TEST_F(ExtensionBadgedIconImageTest, ExtensionIconAsyncLoadSuccessCase) { |
+ GURL icon_url; // No custom icon. |
+ ExtensionBadgedIconImageTestImpl impl(icon_url, nullptr, nullptr, 32, |
+ GetFakeErrorReporter()); |
+ impl.LoadResources(); |
+ |
+ // Extension icon is loaded as async. |
+ gfx::Size icon_size(32, 32); |
+ gfx::ImageSkia extension_icon(new BadgedImageSource(icon_size, SK_ColorGREEN), |
+ icon_size); |
+ impl.LoadExtensionIconAsync(extension_icon); |
+ |
+ gfx::ImageSkia expected_image(new BadgedImageSource(icon_size, SK_ColorGREEN), |
+ icon_size); |
+ ASSERT_TRUE(IsEqual(expected_image, impl)); |
+} |
+ |
+TEST_F(ExtensionBadgedIconImageTest, WithCustomIconSuccessCase) { |
+ GURL icon_url(kTestCustomIconURL); |
+ ExtensionBadgedIconImageTestImpl impl(icon_url, nullptr, extension_.get(), 32, |
+ GetFakeErrorReporter()); |
+ ASSERT_FALSE(impl.IsLoadExtensionIconResourceCalled()); |
+ impl.LoadResources(); |
+ |
+ // Asserts that LoadExtensionIconResource is called. |
+ ASSERT_TRUE(impl.IsLoadExtensionIconResourceCalled()); |
+ |
+ // Load custom icon as async. |
+ gfx::Size icon_size(32, 32); |
+ gfx::ImageSkia custom_icon(new BadgedImageSource(icon_size, SK_ColorGREEN), |
+ icon_size); |
+ impl.CallOnCustomIconLoaded(custom_icon); |
+ |
+ gfx::ImageSkia expected_image( |
+ new BadgedImageSource(icon_size, SK_ColorGREEN, SK_ColorBLACK), |
+ icon_size); |
+ ASSERT_TRUE(IsEqual(expected_image, impl)); |
+} |
+ |
+TEST_F(ExtensionBadgedIconImageTest, InvalidCustomIconUrl) { |
+ // Use a really long URL (for testing the string truncation). |
+ // The URL is from the wrong extension (foo2), so should be rejected. |
+ std::string invalid_url = |
+ "chrome-extension://foo2/bar/" |
+ "901234567890123456789012345678901234567890123456789012345678901234567890" |
+ "1"; |
+ ASSERT_EQ(101U, invalid_url.size()); |
+ |
+ scoped_ptr<FakeErrorReporter> fake_error_reporter = GetFakeErrorReporter(); |
+ GURL icon_url(invalid_url); |
+ ExtensionBadgedIconImageTestImpl impl(icon_url, nullptr, extension_.get(), 32, |
+ fake_error_reporter->Duplicate()); |
+ impl.LoadResources(); |
+ |
+ // Warning message should be provided. |
+ ASSERT_EQ( |
+ "[chrome.launcherSearchProvider.setSearchResults] Invalid icon URL: " |
+ "chrome-extension://foo2/bar/" |
+ "901234567890123456789012345678901234567890123456789012345678901234567..." |
+ ". Must have a valid URL within chrome-extension://foo.", |
+ fake_error_reporter->GetLastWarningMessage()); |
+ |
+ // LoadExtensionIconResource should not be called. |
+ ASSERT_FALSE(impl.IsLoadExtensionIconResourceCalled()); |
+} |
+ |
+TEST_F(ExtensionBadgedIconImageTest, FailedToLoadCustomIcon) { |
+ scoped_ptr<FakeErrorReporter> fake_error_reporter = GetFakeErrorReporter(); |
+ GURL icon_url(kTestCustomIconURL); |
+ ExtensionBadgedIconImageTestImpl impl(icon_url, nullptr, extension_.get(), 32, |
+ fake_error_reporter->Duplicate()); |
+ impl.LoadResources(); |
+ ASSERT_TRUE(impl.IsLoadExtensionIconResourceCalled()); |
+ |
+ // Fails to load custom icon by passing an empty image. |
+ gfx::ImageSkia custom_icon; |
+ impl.CallOnCustomIconLoaded(custom_icon); |
+ |
+ // Warning message should be shown. |
+ ASSERT_EQ( |
+ "[chrome.launcherSearchProvider.setSearchResults] Failed to load icon " |
+ "URL: chrome-extension://foo/bar", |
+ fake_error_reporter->GetLastWarningMessage()); |
+ |
+ // Icon should be extension icon. |
+ gfx::Size icon_size(32, 32); |
+ gfx::ImageSkia expected_image(new BadgedImageSource(icon_size, SK_ColorBLACK), |
+ icon_size); |
+ ASSERT_TRUE(IsEqual(expected_image, impl)); |
+} |
+ |
+} // namespace app_list |