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 "components/crx_file/id_util.h"
13#include "content/public/test/test_browser_thread.h"
14#include "extensions/common/extension.h"
15#include "testing/gtest/include/gtest/gtest.h"
16#include "ui/gfx/skia_util.h"
17
18using content::BrowserThread;
19using extensions::Extension;
20using 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.
24class 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.
74class 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.
95SkBitmap GetDefaultIcon() {
96  std::string dummy_id = crx_file::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.
102TEST_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<base::DictionaryValue> manifest(
113      static_cast<base::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(OS_CHROMEOS)
143// Tests loading an icon for a component extension.
144TEST_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<base::DictionaryValue> manifest(
155      static_cast<base::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