15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/json/json_file_value_serializer.h"
6ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "base/message_loop/message_loop.h"
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/path_service.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_icon_manager.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_paths.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/test/base/testing_profile.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/test/test_browser_thread.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "extensions/common/id_util.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/skia_util.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::Extension;
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::Manifest;
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Our test class that takes care of managing the necessary threads for loading
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// extension icons, and waiting for those loads to happen.
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ExtensionIconManagerTest : public testing::Test {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionIconManagerTest() :
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      unwaited_image_loads_(0),
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      waiting_(false),
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ui_thread_(BrowserThread::UI, &ui_loop_),
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      file_thread_(BrowserThread::FILE),
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      io_thread_(BrowserThread::IO) {}
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~ExtensionIconManagerTest() {}
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void ImageLoadObserved() {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    unwaited_image_loads_++;
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (waiting_) {
3890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      base::MessageLoop::current()->Quit();
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void WaitForImageLoad() {
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (unwaited_image_loads_ == 0) {
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      waiting_ = true;
4590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      base::MessageLoop::current()->Run();
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      waiting_ = false;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ASSERT_GT(unwaited_image_loads_, 0);
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    unwaited_image_loads_--;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void SetUp() {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_thread_.Start();
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    io_thread_.Start();
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The number of observed image loads that have not been waited for.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int unwaited_image_loads_;
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Whether we are currently waiting for an image load.
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool waiting_;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  base::MessageLoop ui_loop_;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::TestBrowserThread ui_thread_;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::TestBrowserThread file_thread_;
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::TestBrowserThread io_thread_;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ExtensionIconManagerTest);
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This is a specialization of ExtensionIconManager, with a special override to
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// call back to the test when an icon has completed loading.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class TestIconManager : public ExtensionIconManager {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit TestIconManager(ExtensionIconManagerTest* test) : test_(test) {}
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~TestIconManager() {}
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Overrides the ImageLoader callback, and calls through to the base class'
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // implementation. Then it lets the test know that an image load was observed.
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void OnImageLoaded(const std::string& extension_id,
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             const gfx::Image& image) OVERRIDE {
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ExtensionIconManager::OnImageLoaded(extension_id, image);
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test_->ImageLoadObserved();
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionIconManagerTest* test_;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(TestIconManager);
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the default icon that ExtensionIconManager gives when an extension
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// doesn't have an icon.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SkBitmap GetDefaultIcon() {
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string dummy_id = extensions::id_util::GenerateId("whatever");
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionIconManager manager;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return manager.GetIcon(dummy_id);
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Tests loading an icon for an extension, removing it, then re-loading it.
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ExtensionIconManagerTest, LoadRemoveLoad) {
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<Profile> profile(new TestingProfile());
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap default_icon = GetDefaultIcon();
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath test_dir;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath manifest_path = test_dir.AppendASCII(
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "extensions/image_loading_tracker/app.json");
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  JSONFileValueSerializer serializer(manifest_path);
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<DictionaryValue> manifest(
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL)));
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(manifest.get() != NULL);
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string error;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<Extension> extension(Extension::Create(
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      manifest_path.DirName(), Manifest::INVALID_LOCATION, *manifest.get(),
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Extension::NO_FLAGS, &error));
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(extension.get());
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TestIconManager icon_manager(this);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Load the icon and grab the bitmap.
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  icon_manager.LoadIcon(profile.get(), extension.get());
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WaitForImageLoad();
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap first_icon = icon_manager.GetIcon(extension->id());
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(gfx::BitmapsAreEqual(first_icon, default_icon));
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Remove the icon from the manager.
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_manager.RemoveIcon(extension->id());
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now re-load the icon - we should get the same result bitmap (and not the
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // default icon).
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  icon_manager.LoadIcon(profile.get(), extension.get());
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WaitForImageLoad();
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap second_icon = icon_manager.GetIcon(extension->id());
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(gfx::BitmapsAreEqual(second_icon, default_icon));
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(gfx::BitmapsAreEqual(first_icon, second_icon));
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(FILE_MANAGER_EXTENSION)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Tests loading an icon for a component extension.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ExtensionIconManagerTest, LoadComponentExtensionResource) {
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  scoped_ptr<Profile> profile(new TestingProfile());
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap default_icon = GetDefaultIcon();
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath test_dir;
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath manifest_path = test_dir.AppendASCII(
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "extensions/file_manager/app.json");
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  JSONFileValueSerializer serializer(manifest_path);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<DictionaryValue> manifest(
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL)));
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(manifest.get() != NULL);
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string error;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<Extension> extension(Extension::Create(
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      manifest_path.DirName(), Manifest::COMPONENT, *manifest.get(),
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Extension::NO_FLAGS, &error));
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(extension.get());
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TestIconManager icon_manager(this);
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Load the icon and grab the bitmap.
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  icon_manager.LoadIcon(profile.get(), extension.get());
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WaitForImageLoad();
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap first_icon = icon_manager.GetIcon(extension->id());
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(gfx::BitmapsAreEqual(first_icon, default_icon));
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Remove the icon from the manager.
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_manager.RemoveIcon(extension->id());
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now re-load the icon - we should get the same result bitmap (and not the
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // default icon).
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  icon_manager.LoadIcon(profile.get(), extension.get());
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WaitForImageLoad();
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap second_icon = icon_manager.GetIcon(extension->id());
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(gfx::BitmapsAreEqual(second_icon, default_icon));
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(gfx::BitmapsAreEqual(first_icon, second_icon));
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
184