1 // Copyright (c) 2012 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 5 #include "base/json/json_file_value_serializer.h" 6 #include "base/message_loop/message_loop.h" 7 #include "base/path_service.h" 8 #include "base/values.h" 9 #include "chrome/browser/extensions/extension_icon_manager.h" 10 #include "chrome/common/chrome_paths.h" 11 #include "chrome/test/base/testing_profile.h" 12 #include "content/public/test/test_browser_thread.h" 13 #include "extensions/common/extension.h" 14 #include "extensions/common/id_util.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 #include "ui/gfx/skia_util.h" 17 18 using content::BrowserThread; 19 using extensions::Extension; 20 using extensions::Manifest; 21 22 // Our test class that takes care of managing the necessary threads for loading 23 // extension icons, and waiting for those loads to happen. 24 class ExtensionIconManagerTest : public testing::Test { 25 public: 26 ExtensionIconManagerTest() : 27 unwaited_image_loads_(0), 28 waiting_(false), 29 ui_thread_(BrowserThread::UI, &ui_loop_), 30 file_thread_(BrowserThread::FILE), 31 io_thread_(BrowserThread::IO) {} 32 33 virtual ~ExtensionIconManagerTest() {} 34 35 void ImageLoadObserved() { 36 unwaited_image_loads_++; 37 if (waiting_) { 38 base::MessageLoop::current()->Quit(); 39 } 40 } 41 42 void WaitForImageLoad() { 43 if (unwaited_image_loads_ == 0) { 44 waiting_ = true; 45 base::MessageLoop::current()->Run(); 46 waiting_ = false; 47 } 48 ASSERT_GT(unwaited_image_loads_, 0); 49 unwaited_image_loads_--; 50 } 51 52 private: 53 virtual void SetUp() { 54 file_thread_.Start(); 55 io_thread_.Start(); 56 } 57 58 // The number of observed image loads that have not been waited for. 59 int unwaited_image_loads_; 60 61 // Whether we are currently waiting for an image load. 62 bool waiting_; 63 64 base::MessageLoop ui_loop_; 65 content::TestBrowserThread ui_thread_; 66 content::TestBrowserThread file_thread_; 67 content::TestBrowserThread io_thread_; 68 69 DISALLOW_COPY_AND_ASSIGN(ExtensionIconManagerTest); 70 }; 71 72 // This is a specialization of ExtensionIconManager, with a special override to 73 // call back to the test when an icon has completed loading. 74 class TestIconManager : public ExtensionIconManager { 75 public: 76 explicit TestIconManager(ExtensionIconManagerTest* test) : test_(test) {} 77 virtual ~TestIconManager() {} 78 79 // Overrides the ImageLoader callback, and calls through to the base class' 80 // implementation. Then it lets the test know that an image load was observed. 81 virtual void OnImageLoaded(const std::string& extension_id, 82 const gfx::Image& image) OVERRIDE { 83 ExtensionIconManager::OnImageLoaded(extension_id, image); 84 test_->ImageLoadObserved(); 85 } 86 87 private: 88 ExtensionIconManagerTest* test_; 89 90 DISALLOW_COPY_AND_ASSIGN(TestIconManager); 91 }; 92 93 // Returns the default icon that ExtensionIconManager gives when an extension 94 // doesn't have an icon. 95 SkBitmap GetDefaultIcon() { 96 std::string dummy_id = extensions::id_util::GenerateId("whatever"); 97 ExtensionIconManager manager; 98 return manager.GetIcon(dummy_id); 99 } 100 101 // Tests loading an icon for an extension, removing it, then re-loading it. 102 TEST_F(ExtensionIconManagerTest, LoadRemoveLoad) { 103 scoped_ptr<Profile> profile(new TestingProfile()); 104 SkBitmap default_icon = GetDefaultIcon(); 105 106 base::FilePath test_dir; 107 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir)); 108 base::FilePath manifest_path = test_dir.AppendASCII( 109 "extensions/image_loading_tracker/app.json"); 110 111 JSONFileValueSerializer serializer(manifest_path); 112 scoped_ptr<DictionaryValue> manifest( 113 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL))); 114 ASSERT_TRUE(manifest.get() != NULL); 115 116 std::string error; 117 scoped_refptr<Extension> extension(Extension::Create( 118 manifest_path.DirName(), Manifest::INVALID_LOCATION, *manifest.get(), 119 Extension::NO_FLAGS, &error)); 120 ASSERT_TRUE(extension.get()); 121 TestIconManager icon_manager(this); 122 123 // Load the icon and grab the bitmap. 124 icon_manager.LoadIcon(profile.get(), extension.get()); 125 WaitForImageLoad(); 126 SkBitmap first_icon = icon_manager.GetIcon(extension->id()); 127 EXPECT_FALSE(gfx::BitmapsAreEqual(first_icon, default_icon)); 128 129 // Remove the icon from the manager. 130 icon_manager.RemoveIcon(extension->id()); 131 132 // Now re-load the icon - we should get the same result bitmap (and not the 133 // default icon). 134 icon_manager.LoadIcon(profile.get(), extension.get()); 135 WaitForImageLoad(); 136 SkBitmap second_icon = icon_manager.GetIcon(extension->id()); 137 EXPECT_FALSE(gfx::BitmapsAreEqual(second_icon, default_icon)); 138 139 EXPECT_TRUE(gfx::BitmapsAreEqual(first_icon, second_icon)); 140 } 141 142 #if defined(FILE_MANAGER_EXTENSION) 143 // Tests loading an icon for a component extension. 144 TEST_F(ExtensionIconManagerTest, LoadComponentExtensionResource) { 145 scoped_ptr<Profile> profile(new TestingProfile()); 146 SkBitmap default_icon = GetDefaultIcon(); 147 148 base::FilePath test_dir; 149 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir)); 150 base::FilePath manifest_path = test_dir.AppendASCII( 151 "extensions/file_manager/app.json"); 152 153 JSONFileValueSerializer serializer(manifest_path); 154 scoped_ptr<DictionaryValue> manifest( 155 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL))); 156 ASSERT_TRUE(manifest.get() != NULL); 157 158 std::string error; 159 scoped_refptr<Extension> extension(Extension::Create( 160 manifest_path.DirName(), Manifest::COMPONENT, *manifest.get(), 161 Extension::NO_FLAGS, &error)); 162 ASSERT_TRUE(extension.get()); 163 164 TestIconManager icon_manager(this); 165 // Load the icon and grab the bitmap. 166 icon_manager.LoadIcon(profile.get(), extension.get()); 167 WaitForImageLoad(); 168 SkBitmap first_icon = icon_manager.GetIcon(extension->id()); 169 EXPECT_FALSE(gfx::BitmapsAreEqual(first_icon, default_icon)); 170 171 // Remove the icon from the manager. 172 icon_manager.RemoveIcon(extension->id()); 173 174 // Now re-load the icon - we should get the same result bitmap (and not the 175 // default icon). 176 icon_manager.LoadIcon(profile.get(), extension.get()); 177 WaitForImageLoad(); 178 SkBitmap second_icon = icon_manager.GetIcon(extension->id()); 179 EXPECT_FALSE(gfx::BitmapsAreEqual(second_icon, default_icon)); 180 181 EXPECT_TRUE(gfx::BitmapsAreEqual(first_icon, second_icon)); 182 } 183 #endif 184