root/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. registry_
  2. SetUp
  3. AddRule
  4. AddRule
  5. RemoveRule
  6. GetNumberOfRules
  7. GetNumberOfRules
  8. profile
  9. TEST_F
  10. TEST_F
  11. TEST_F
  12. TEST_F
  13. TEST_F
  14. TEST_F
  15. TEST_F
  16. TEST_F
  17. TEST_F
  18. TEST_F

// 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/api/declarative/rules_registry.h"

// Here we test the TestRulesRegistry which is the simplest possible
// implementation of RulesRegistryWithCache as a proxy for
// RulesRegistryWithCache.

#include "base/command_line.h"
#include "base/run_loop.h"
#include "chrome/browser/extensions/api/declarative/rules_cache_delegate.h"
#include "chrome/browser/extensions/api/declarative/test_rules_registry.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/test_extension_environment.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/common/extensions/extension_test_util.h"
#include "chrome/common/extensions/features/feature_channel.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/value_store/testing_value_store.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_constants.h"
#include "testing/gtest/include/gtest/gtest.h"

using extension_test_util::LoadManifestUnchecked;

namespace {
const char kRuleId[] = "rule";
const char kRule2Id[] = "rule2";
}

namespace extensions {

class RulesRegistryWithCacheTest : public testing::Test {
 public:
  RulesRegistryWithCacheTest()
      : cache_delegate_(/*log_storage_init_delay=*/false),
        registry_(new TestRulesRegistry(profile(),
                                        /*event_name=*/"",
                                        content::BrowserThread::UI,
                                        &cache_delegate_,
                                        RulesRegistry::WebViewKey(0, 0))) {}

  virtual void SetUp() {
    env_.GetExtensionPrefs();  // Force creation before adding extensions.
    // Note that env_.MakeExtension below also forces the creation of
    // ExtensionService.

    base::DictionaryValue manifest_extra;
    std::string key;
    CHECK(Extension::ProducePEM("test extension 1", &key));
    manifest_extra.SetString(manifest_keys::kPublicKey, key);
    extension1_ = env_.MakeExtension(manifest_extra);
    CHECK(extension1_.get());

    // Different "key" values for the two extensions ensure a different ID.
    CHECK(Extension::ProducePEM("test extension 2", &key));
    manifest_extra.SetString(manifest_keys::kPublicKey, key);
    extension2_ = env_.MakeExtension(manifest_extra);
    CHECK(extension2_.get());
    CHECK_NE(extension2_->id(), extension1_->id());
  }

  virtual ~RulesRegistryWithCacheTest() {}

  std::string AddRule(const std::string& extension_id,
                      const std::string& rule_id,
                      TestRulesRegistry* registry) {
    std::vector<linked_ptr<RulesRegistry::Rule> > add_rules;
    add_rules.push_back(make_linked_ptr(new RulesRegistry::Rule));
    add_rules[0]->id.reset(new std::string(rule_id));
    return registry->AddRules(extension_id, add_rules);
  }

  std::string AddRule(const std::string& extension_id,
                      const std::string& rule_id) {
    return AddRule(extension_id, rule_id, registry_.get());
  }

  std::string RemoveRule(const std::string& extension_id,
                         const std::string& rule_id) {
    std::vector<std::string> remove_rules;
    remove_rules.push_back(rule_id);
    return registry_->RemoveRules(extension_id, remove_rules);
  }

  int GetNumberOfRules(const std::string& extension_id,
                       TestRulesRegistry* registry) {
    std::vector<linked_ptr<RulesRegistry::Rule> > get_rules;
    registry->GetAllRules(extension_id, &get_rules);
    return get_rules.size();
  }

  int GetNumberOfRules(const std::string& extension_id) {
    return GetNumberOfRules(extension_id, registry_.get());
  }

  TestingProfile* profile() const { return env_.profile(); }

 protected:
  TestExtensionEnvironment env_;
  RulesCacheDelegate cache_delegate_;
  scoped_refptr<TestRulesRegistry> registry_;
  scoped_refptr<const Extension> extension1_;
  scoped_refptr<const Extension> extension2_;
};

TEST_F(RulesRegistryWithCacheTest, AddRules) {
  // Check that nothing happens if the concrete RulesRegistry refuses to insert
  // the rules.
  registry_->SetResult("Error");
  EXPECT_EQ("Error", AddRule(extension1_->id(), kRuleId));
  EXPECT_EQ(0, GetNumberOfRules(extension1_->id()));
  registry_->SetResult(std::string());

  // Check that rules can be inserted.
  EXPECT_EQ("", AddRule(extension1_->id(), kRule2Id));
  EXPECT_EQ(1, GetNumberOfRules(extension1_->id()));

  // Check that rules cannot be inserted twice with the same kRuleId.
  EXPECT_NE("", AddRule(extension1_->id(), kRuleId));
  EXPECT_EQ(1, GetNumberOfRules(extension1_->id()));

  // Check that different extensions may use the same kRuleId.
  EXPECT_EQ("", AddRule(extension2_->id(), kRuleId));
  EXPECT_EQ(1, GetNumberOfRules(extension1_->id()));
  EXPECT_EQ(1, GetNumberOfRules(extension2_->id()));
}

TEST_F(RulesRegistryWithCacheTest, RemoveRules) {
  // Prime registry.
  EXPECT_EQ("", AddRule(extension1_->id(), kRuleId));
  EXPECT_EQ("", AddRule(extension2_->id(), kRuleId));
  EXPECT_EQ(1, GetNumberOfRules(extension1_->id()));
  EXPECT_EQ(1, GetNumberOfRules(extension2_->id()));

  // Check that nothing happens if the concrete RuleRegistry refuses to remove
  // the rules.
  registry_->SetResult("Error");
  EXPECT_EQ("Error", RemoveRule(extension1_->id(), kRuleId));
  EXPECT_EQ(1, GetNumberOfRules(extension1_->id()));
  registry_->SetResult(std::string());

  // Check that nothing happens if a rule does not exist.
  EXPECT_EQ("", RemoveRule(extension1_->id(), "unknown_rule"));
  EXPECT_EQ(1, GetNumberOfRules(extension1_->id()));

  // Check that rules may be removed and only for the correct extension.
  EXPECT_EQ("", RemoveRule(extension1_->id(), kRuleId));
  EXPECT_EQ(0, GetNumberOfRules(extension1_->id()));
  EXPECT_EQ(1, GetNumberOfRules(extension2_->id()));
}

TEST_F(RulesRegistryWithCacheTest, RemoveAllRules) {
  // Prime registry.
  EXPECT_EQ("", AddRule(extension1_->id(), kRuleId));
  EXPECT_EQ("", AddRule(extension1_->id(), kRule2Id));
  EXPECT_EQ("", AddRule(extension2_->id(), kRuleId));
  EXPECT_EQ(2, GetNumberOfRules(extension1_->id()));
  EXPECT_EQ(1, GetNumberOfRules(extension2_->id()));

  // Check that nothing happens if the concrete RuleRegistry refuses to remove
  // the rules.
  registry_->SetResult("Error");
  EXPECT_EQ("Error", registry_->RemoveAllRules(extension1_->id()));
  EXPECT_EQ(2, GetNumberOfRules(extension1_->id()));
  registry_->SetResult(std::string());

  // Check that rules may be removed and only for the correct extension.
  EXPECT_EQ("", registry_->RemoveAllRules(extension1_->id()));
  EXPECT_EQ(0, GetNumberOfRules(extension1_->id()));
  EXPECT_EQ(1, GetNumberOfRules(extension2_->id()));
}

TEST_F(RulesRegistryWithCacheTest, GetRules) {
  // Prime registry.
  EXPECT_EQ("", AddRule(extension1_->id(), kRuleId));
  EXPECT_EQ("", AddRule(extension1_->id(), kRule2Id));
  EXPECT_EQ("", AddRule(extension2_->id(), kRuleId));

  // Check that we get the correct rule and unknown rules are ignored.
  std::vector<std::string> rules_to_get;
  rules_to_get.push_back(kRuleId);
  rules_to_get.push_back("unknown_rule");
  std::vector<linked_ptr<RulesRegistry::Rule> > gotten_rules;
  registry_->GetRules(extension1_->id(), rules_to_get, &gotten_rules);
  ASSERT_EQ(1u, gotten_rules.size());
  ASSERT_TRUE(gotten_rules[0]->id.get());
  EXPECT_EQ(kRuleId, *(gotten_rules[0]->id));
}

TEST_F(RulesRegistryWithCacheTest, GetAllRules) {
  // Prime registry.
  EXPECT_EQ("", AddRule(extension1_->id(), kRuleId));
  EXPECT_EQ("", AddRule(extension1_->id(), kRule2Id));
  EXPECT_EQ("", AddRule(extension2_->id(), kRuleId));

  // Check that we get the correct rules.
  std::vector<linked_ptr<RulesRegistry::Rule> > gotten_rules;
  registry_->GetAllRules(extension1_->id(), &gotten_rules);
  EXPECT_EQ(2u, gotten_rules.size());
  ASSERT_TRUE(gotten_rules[0]->id.get());
  ASSERT_TRUE(gotten_rules[1]->id.get());
  EXPECT_TRUE( (kRuleId == *(gotten_rules[0]->id) &&
                kRule2Id == *(gotten_rules[1]->id)) ||
               (kRuleId == *(gotten_rules[1]->id) &&
                kRule2Id == *(gotten_rules[0]->id)) );
}

TEST_F(RulesRegistryWithCacheTest, OnExtensionUninstalled) {
  // Prime registry.
  EXPECT_EQ("", AddRule(extension1_->id(), kRuleId));
  EXPECT_EQ("", AddRule(extension2_->id(), kRuleId));

  // Check that the correct rules are removed.
  registry_->OnExtensionUninstalled(extension1_->id());
  EXPECT_EQ(0, GetNumberOfRules(extension1_->id()));
  EXPECT_EQ(1, GetNumberOfRules(extension2_->id()));
}

TEST_F(RulesRegistryWithCacheTest, DeclarativeRulesStored) {
  ExtensionPrefs* extension_prefs = env_.GetExtensionPrefs();
  // The value store is first created during GetExtensionService.
  TestingValueStore* store = env_.GetExtensionSystem()->value_store();

  const std::string event_name("testEvent");
  const std::string rules_stored_key(
      RulesCacheDelegate::GetRulesStoredKey(
          event_name, profile()->IsOffTheRecord()));
  scoped_ptr<RulesCacheDelegate> cache_delegate(new RulesCacheDelegate(false));
  scoped_refptr<RulesRegistry> registry(new TestRulesRegistry(
      profile(), event_name, content::BrowserThread::UI,
      cache_delegate.get(),
      RulesRegistry::WebViewKey(0, 0)));

  // 1. Test the handling of preferences.
  // Default value is always true.
  EXPECT_TRUE(cache_delegate->GetDeclarativeRulesStored(extension1_->id()));

  extension_prefs->UpdateExtensionPref(
      extension1_->id(), rules_stored_key, new base::FundamentalValue(false));
  EXPECT_FALSE(cache_delegate->GetDeclarativeRulesStored(extension1_->id()));

  extension_prefs->UpdateExtensionPref(
      extension1_->id(), rules_stored_key, new base::FundamentalValue(true));
  EXPECT_TRUE(cache_delegate->GetDeclarativeRulesStored(extension1_->id()));

  // 2. Test writing behavior.
  int write_count = store->write_count();

  scoped_ptr<base::ListValue> value(new base::ListValue);
  value->AppendBoolean(true);
  cache_delegate->WriteToStorage(extension1_->id(),
                                 value.PassAs<base::Value>());
  EXPECT_TRUE(cache_delegate->GetDeclarativeRulesStored(extension1_->id()));
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(write_count + 1, store->write_count());
  write_count = store->write_count();

  value.reset(new base::ListValue);
  cache_delegate->WriteToStorage(extension1_->id(),
                                 value.PassAs<base::Value>());
  EXPECT_FALSE(cache_delegate->GetDeclarativeRulesStored(extension1_->id()));
  base::RunLoop().RunUntilIdle();
  // No rules currently, but previously there were, so we expect a write.
  EXPECT_EQ(write_count + 1, store->write_count());
  write_count = store->write_count();

  value.reset(new base::ListValue);
  cache_delegate->WriteToStorage(extension1_->id(),
                                 value.PassAs<base::Value>());
  EXPECT_FALSE(cache_delegate->GetDeclarativeRulesStored(extension1_->id()));
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(write_count, store->write_count());

  // 3. Test reading behavior.
  int read_count = store->read_count();

  cache_delegate->SetDeclarativeRulesStored(extension1_->id(), false);
  cache_delegate->ReadFromStorage(extension1_->id());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(read_count, store->read_count());
  read_count = store->read_count();

  cache_delegate->SetDeclarativeRulesStored(extension1_->id(), true);
  cache_delegate->ReadFromStorage(extension1_->id());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(read_count + 1, store->read_count());
}

// Test that each registry has its own "are some rules stored" flag.
TEST_F(RulesRegistryWithCacheTest, RulesStoredFlagMultipleRegistries) {
  ExtensionPrefs* extension_prefs = env_.GetExtensionPrefs();

  const std::string event_name1("testEvent1");
  const std::string event_name2("testEvent2");
  const std::string rules_stored_key1(
      RulesCacheDelegate::GetRulesStoredKey(
          event_name1, profile()->IsOffTheRecord()));
  const std::string rules_stored_key2(
      RulesCacheDelegate::GetRulesStoredKey(
          event_name2, profile()->IsOffTheRecord()));
  scoped_ptr<RulesCacheDelegate> cache_delegate1(new RulesCacheDelegate(false));
  scoped_refptr<RulesRegistry> registry1(new TestRulesRegistry(
      profile(), event_name1, content::BrowserThread::UI,
      cache_delegate1.get(),
      RulesRegistry::WebViewKey(0, 0)));

  scoped_ptr<RulesCacheDelegate> cache_delegate2(new RulesCacheDelegate(false));
  scoped_refptr<RulesRegistry> registry2(new TestRulesRegistry(
      profile(), event_name2, content::BrowserThread::UI,
      cache_delegate2.get(),
      RulesRegistry::WebViewKey(0, 0)));

  // Checkt the correct default values.
  EXPECT_TRUE(cache_delegate1->GetDeclarativeRulesStored(extension1_->id()));
  EXPECT_TRUE(cache_delegate2->GetDeclarativeRulesStored(extension1_->id()));

  // Update the flag for the first registry.
  extension_prefs->UpdateExtensionPref(
      extension1_->id(), rules_stored_key1, new base::FundamentalValue(false));
  EXPECT_FALSE(cache_delegate1->GetDeclarativeRulesStored(extension1_->id()));
  EXPECT_TRUE(cache_delegate2->GetDeclarativeRulesStored(extension1_->id()));
}

TEST_F(RulesRegistryWithCacheTest, RulesPreservedAcrossRestart) {
  // This test makes sure that rules are restored from the rule store
  // on registry (in particular, browser) restart.

  // TODO(vabr): Once some API using declarative rules enters the stable
  // channel, make sure to use that API here, and remove |channel|.
  ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_UNKNOWN);

  ExtensionService* extension_service = env_.GetExtensionService();

  // 1. Add an extension, before rules registry gets created.
  std::string error;
  scoped_refptr<Extension> extension(
      LoadManifestUnchecked("permissions",
                            "web_request_all_host_permissions.json",
                            Manifest::INVALID_LOCATION,
                            Extension::NO_FLAGS,
                            extension1_->id(),
                            &error));
  ASSERT_TRUE(error.empty());
  extension_service->AddExtension(extension.get());
  EXPECT_TRUE(extension_service->extensions()->Contains(extension->id()));
  EXPECT_TRUE(
      extension->HasAPIPermission(APIPermission::kDeclarativeWebRequest));
  env_.GetExtensionSystem()->SetReady();

  // 2. First run, adding a rule for the extension.
  scoped_ptr<RulesCacheDelegate> cache_delegate(new RulesCacheDelegate(false));
  scoped_refptr<TestRulesRegistry> registry(new TestRulesRegistry(
      profile(),
      "testEvent",
      content::BrowserThread::UI,
      cache_delegate.get(),
      RulesRegistry::WebViewKey(0, 0)));

  AddRule(extension1_->id(), kRuleId, registry.get());
  base::RunLoop().RunUntilIdle();  // Posted tasks store the added rule.
  EXPECT_EQ(1, GetNumberOfRules(extension1_->id(), registry.get()));

  // 3. Restart the TestRulesRegistry and see the rule still there.
  cache_delegate.reset(new RulesCacheDelegate(false));
  registry = new TestRulesRegistry(
      profile(),
      "testEvent",
      content::BrowserThread::UI,
      cache_delegate.get(),
      RulesRegistry::WebViewKey(0, 0));

  base::RunLoop().RunUntilIdle();  // Posted tasks retrieve the stored rule.
  EXPECT_EQ(1, GetNumberOfRules(extension1_->id(), registry.get()));
}

TEST_F(RulesRegistryWithCacheTest, ConcurrentStoringOfRules) {
  // When an extension updates its rules, the new set of rules is stored to disk
  // with some delay. While it is acceptable for a quick series of updates for a
  // single extension to only write the last one, we should never forget to
  // write a rules update for extension A, just because it is immediately
  // followed by a rules update for extension B.
  extensions::TestExtensionSystem* system = env_.GetExtensionSystem();
  TestingValueStore* store = system->value_store();

  int write_count = store->write_count();
  EXPECT_EQ("", AddRule(extension1_->id(), kRuleId));
  EXPECT_EQ("", AddRule(extension2_->id(), kRule2Id));
  env_.GetExtensionSystem()->SetReady();
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(write_count + 2, store->write_count());
}

}  //  namespace extensions

/* [<][>][^][v][top][bottom][index][help] */