text: // Copyright (c) 2012 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/extensions/component_loader.h"
  
  #include <string>
  
  #include "base/file_util.h"
  #include "base/path_service.h"
  #include "base/prefs/pref_registry_simple.h"
  #include "chrome/browser/extensions/test_extension_service.h"
  #include "chrome/common/chrome_paths.h"
  #include "chrome/common/extensions/extension.h"
  #include "chrome/common/extensions/extension_set.h"
  #include "chrome/common/pref_names.h"
  #include "chrome/test/base/testing_pref_service_syncable.h"
  #include "components/user_prefs/pref_registry_syncable.h"
  #include "extensions/common/constants.h"
  #include "extensions/common/manifest_handlers/background_info.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  namespace extensions {
  
  namespace {
  
  class MockExtensionService : public TestExtensionService {
   private:
    bool ready_;
    size_t unloaded_count_;
    ExtensionSet extension_set_;
  
   public:
    MockExtensionService() : ready_(false), unloaded_count_(0) {
    }
  
    virtual void AddComponentExtension(const Extension* extension) OVERRIDE {
      EXPECT_FALSE(extension_set_.Contains(extension->id()));
      // ExtensionService must become the owner of the extension object.
      extension_set_.Insert(extension);
    }
  
    virtual void UnloadExtension(
        const std::string& extension_id,
        UnloadedExtensionInfo::Reason reason) OVERRIDE {
      ASSERT_TRUE(extension_set_.Contains(extension_id));
      // Remove the extension with the matching id.
      extension_set_.Remove(extension_id);
      unloaded_count_++;
    }
  
    virtual void RemoveComponentExtension(const std::string & extension_id)
        OVERRIDE {
      UnloadExtension(extension_id, UnloadedExtensionInfo::REASON_DISABLE);
    }
  
    virtual bool is_ready() OVERRIDE {
      return ready_;
    }
  
    virtual const ExtensionSet* extensions() const OVERRIDE {
      return &extension_set_;
    }
  
    void set_ready(bool ready) {
      ready_ = ready;
    }
  
    size_t unloaded_count() const {
      return unloaded_count_;
    }
  
    void clear_extensions() {
      extension_set_.Clear();
    }
  };
  
  }  // namespace
  
  class ComponentLoaderTest : public testing::Test {
   public:
    ComponentLoaderTest()
        // Note: we pass the same pref service here, to stand in for both
        // user prefs and local state.
        : component_loader_(&extension_service_, &prefs_, &local_state_) {
    }
  
    virtual void SetUp() OVERRIDE {
      extension_path_ =
          GetBasePath().AppendASCII("good")
                       .AppendASCII("Extensions")
                       .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
                       .AppendASCII("1.0.0.0");
  
      // Read in the extension manifest.
      ASSERT_TRUE(base::ReadFileToString(
          extension_path_.Append(kManifestFilename),
          &manifest_contents_));
  
      // Register the local state prefs.
  #if defined(OS_CHROMEOS)
      local_state_.registry()->RegisterBooleanPref(
          prefs::kSpokenFeedbackEnabled, false);
  #endif
    }
  
   protected:
    MockExtensionService extension_service_;
    TestingPrefServiceSyncable prefs_;
    TestingPrefServiceSimple local_state_;
    ComponentLoader component_loader_;
  
    // The root directory of the text extension.
    base::FilePath extension_path_;
  
    // The contents of the text extension's manifest file.
    std::string manifest_contents_;
  
    base::FilePath GetBasePath() {
      base::FilePath test_data_dir;
      PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
      return test_data_dir.AppendASCII("extensions");
    }
  };
  
  TEST_F(ComponentLoaderTest, ParseManifest) {
    scoped_ptr<DictionaryValue> manifest;
  
    // Test invalid JSON.
    manifest.reset(
        component_loader_.ParseManifest("{ 'test': 3 } invalid"));
    EXPECT_FALSE(manifest.get());
  
    // Test manifests that are valid JSON, but don't have an object literal
    // at the root. ParseManifest() should always return NULL.
  
    manifest.reset(component_loader_.ParseManifest(std::string()));
    EXPECT_FALSE(manifest.get());
  
    manifest.reset(component_loader_.ParseManifest("[{ \"foo\": 3 }]"));
    EXPECT_FALSE(manifest.get());
  
    manifest.reset(component_loader_.ParseManifest("\"Test\""));
    EXPECT_FALSE(manifest.get());
  
    manifest.reset(component_loader_.ParseManifest("42"));
    EXPECT_FALSE(manifest.get());
  
    manifest.reset(component_loader_.ParseManifest("true"));
    EXPECT_FALSE(manifest.get());
  
    manifest.reset(component_loader_.ParseManifest("false"));
    EXPECT_FALSE(manifest.get());
  
    manifest.reset(component_loader_.ParseManifest("null"));
    EXPECT_FALSE(manifest.get());
  
    // Test parsing valid JSON.
  
    int value = 0;
    manifest.reset(component_loader_.ParseManifest(
        "{ \"test\": { \"one\": 1 }, \"two\": 2 }"));
    ASSERT_TRUE(manifest.get());
    EXPECT_TRUE(manifest->GetInteger("test.one", &value));
    EXPECT_EQ(1, value);
    ASSERT_TRUE(manifest->GetInteger("two", &value));
    EXPECT_EQ(2, value);
  
    std::string string_value;
    manifest.reset(component_loader_.ParseManifest(manifest_contents_));
    ASSERT_TRUE(manifest->GetString("background.page", &string_value));
    EXPECT_EQ("backgroundpage.html", string_value);
  }
  
  // Test that the extension isn't loaded if the extension service isn't ready.
  TEST_F(ComponentLoaderTest, AddWhenNotReady) {
    extension_service_.set_ready(false);
    std::string extension_id =
        component_loader_.Add(manifest_contents_, extension_path_);
    EXPECT_NE("", extension_id);
    EXPECT_EQ(0u, extension_service_.extensions()->size());
  }
  
  // Test that it *is* loaded when the extension service *is* ready.
  TEST_F(ComponentLoaderTest, AddWhenReady) {
    extension_service_.set_ready(true);
    std::string extension_id =
        component_loader_.Add(manifest_contents_, extension_path_);
    EXPECT_NE("", extension_id);
    EXPECT_EQ(1u, extension_service_.extensions()->size());
    EXPECT_TRUE(extension_service_.extensions()->GetByID(extension_id));
  }
  
  TEST_F(ComponentLoaderTest, Remove) {
    extension_service_.set_ready(false);
  
    // Removing an extension that was never added should be ok.
    component_loader_.Remove(extension_path_);
    EXPECT_EQ(0u, extension_service_.extensions()->size());
  
    // Try adding and removing before LoadAll() is called.
    component_loader_.Add(manifest_contents_, extension_path_);
    component_loader_.Remove(extension_path_);
    component_loader_.LoadAll();
    EXPECT_EQ(0u, extension_service_.extensions()->size());
  
    // Load an extension, and check that it's unloaded when Remove() is called.
    extension_service_.set_ready(true);
    std::string extension_id =
        component_loader_.Add(manifest_contents_, extension_path_);
    EXPECT_EQ(1u, extension_service_.extensions()->size());
    component_loader_.Remove(extension_path_);
    EXPECT_EQ(0u, extension_service_.extensions()->size());
  
    // And after calling LoadAll(), it shouldn't get loaded.
    component_loader_.LoadAll();
    EXPECT_EQ(0u, extension_service_.extensions()->size());
  }
  
  TEST_F(ComponentLoaderTest, LoadAll) {
    extension_service_.set_ready(false);
  
    // No extensions should be loaded if none were added.
    component_loader_.LoadAll();
    EXPECT_EQ(0u, extension_service_.extensions()->size());
  
    // Use LoadAll() to load the default extensions.
    component_loader_.AddDefaultComponentExtensions(false);
    component_loader_.LoadAll();
    unsigned int default_count = extension_service_.extensions()->size();
  
    // Clear the list of loaded extensions, and reload with one more.
    extension_service_.clear_extensions();
    component_loader_.Add(manifest_contents_, extension_path_);
    component_loader_.LoadAll();
  
    EXPECT_EQ(default_count + 1, extension_service_.extensions()->size());
  }
  
  TEST_F(ComponentLoaderTest, AddOrReplace) {
    EXPECT_EQ(0u, component_loader_.registered_extensions_count());
    component_loader_.AddDefaultComponentExtensions(false);
    size_t const default_count = component_loader_.registered_extensions_count();
    base::FilePath known_extension = GetBasePath()
        .AppendASCII("override_component_extension");
    base::FilePath unknow_extension = extension_path_;
    base::FilePath invalid_extension = GetBasePath().AppendASCII("bad");
  
    // Replace a default component extension.
    component_loader_.AddOrReplace(known_extension);
    EXPECT_EQ(default_count,
              component_loader_.registered_extensions_count());
  
    // Add a new component extension.
    component_loader_.AddOrReplace(unknow_extension);
    EXPECT_EQ(default_count + 1,
              component_loader_.registered_extensions_count());
  
    extension_service_.set_ready(true);
    component_loader_.LoadAll();
  
    EXPECT_EQ(default_count + 1, extension_service_.extensions()->size());
    EXPECT_EQ(0u, extension_service_.unloaded_count());
  
    // replace loaded component extension.
    component_loader_.AddOrReplace(known_extension);
    EXPECT_EQ(default_count + 1, extension_service_.extensions()->size());
    EXPECT_EQ(1u, extension_service_.unloaded_count());
  
    // Add an invalid component extension.
    std::string extension_id = component_loader_.AddOrReplace(invalid_extension);
    EXPECT_TRUE(extension_id.empty());
  }
  
  }  // namespace extensions
  