diff options
Diffstat (limited to 'chromium/extensions/browser/api/declarative/declarative_rule_unittest.cc')
-rw-r--r-- | chromium/extensions/browser/api/declarative/declarative_rule_unittest.cc | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/chromium/extensions/browser/api/declarative/declarative_rule_unittest.cc b/chromium/extensions/browser/api/declarative/declarative_rule_unittest.cc new file mode 100644 index 00000000000..93ec58b4026 --- /dev/null +++ b/chromium/extensions/browser/api/declarative/declarative_rule_unittest.cc @@ -0,0 +1,433 @@ +// Copyright (c) 2013 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 "extensions/browser/api/declarative/declarative_rule.h" + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "base/test/values_test_util.h" +#include "base/values.h" +#include "components/url_matcher/url_matcher_constants.h" +#include "extensions/common/extension_builder.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::test::ParseJson; +using url_matcher::URLMatcher; +using url_matcher::URLMatcherConditionFactory; +using url_matcher::URLMatcherConditionSet; + +namespace extensions { + +namespace { + +scoped_ptr<base::DictionaryValue> SimpleManifest() { + return DictionaryBuilder() + .Set("name", "extension") + .Set("manifest_version", 2) + .Set("version", "1.0") + .Build(); +} + +} // namespace + +struct RecordingCondition { + typedef int MatchData; + + URLMatcherConditionFactory* factory; + scoped_ptr<base::Value> value; + + void GetURLMatcherConditionSets( + URLMatcherConditionSet::Vector* condition_sets) const { + // No condition sets. + } + + static scoped_ptr<RecordingCondition> Create( + const Extension* extension, + URLMatcherConditionFactory* url_matcher_condition_factory, + const base::Value& condition, + std::string* error) { + const base::DictionaryValue* dict = NULL; + if (condition.GetAsDictionary(&dict) && dict->HasKey("bad_key")) { + *error = "Found error key"; + return scoped_ptr<RecordingCondition>(); + } + + scoped_ptr<RecordingCondition> result(new RecordingCondition()); + result->factory = url_matcher_condition_factory; + result->value.reset(condition.DeepCopy()); + return result; + } +}; +typedef DeclarativeConditionSet<RecordingCondition> RecordingConditionSet; + +TEST(DeclarativeConditionTest, ErrorConditionSet) { + URLMatcher matcher; + RecordingConditionSet::Values conditions; + conditions.push_back(ParseJson("{\"key\": 1}")); + conditions.push_back(ParseJson("{\"bad_key\": 2}")); + + std::string error; + scoped_ptr<RecordingConditionSet> result = RecordingConditionSet::Create( + NULL, matcher.condition_factory(), conditions, &error); + EXPECT_EQ("Found error key", error); + ASSERT_FALSE(result); +} + +TEST(DeclarativeConditionTest, CreateConditionSet) { + URLMatcher matcher; + RecordingConditionSet::Values conditions; + conditions.push_back(ParseJson("{\"key\": 1}")); + conditions.push_back(ParseJson("[\"val1\", 2]")); + + // Test insertion + std::string error; + scoped_ptr<RecordingConditionSet> result = RecordingConditionSet::Create( + NULL, matcher.condition_factory(), conditions, &error); + EXPECT_EQ("", error); + ASSERT_TRUE(result); + EXPECT_EQ(2u, result->conditions().size()); + + EXPECT_EQ(matcher.condition_factory(), result->conditions()[0]->factory); + EXPECT_TRUE(ParseJson("{\"key\": 1}")->Equals( + result->conditions()[0]->value.get())); +} + +struct FulfillableCondition { + struct MatchData { + int value; + const std::set<URLMatcherConditionSet::ID>& url_matches; + }; + + scoped_refptr<URLMatcherConditionSet> condition_set; + int condition_set_id; + int max_value; + + URLMatcherConditionSet::ID url_matcher_condition_set_id() const { + return condition_set_id; + } + + scoped_refptr<URLMatcherConditionSet> url_matcher_condition_set() const { + return condition_set; + } + + void GetURLMatcherConditionSets( + URLMatcherConditionSet::Vector* condition_sets) const { + if (condition_set.get()) + condition_sets->push_back(condition_set); + } + + bool IsFulfilled(const MatchData& match_data) const { + if (condition_set_id != -1 && + !ContainsKey(match_data.url_matches, condition_set_id)) + return false; + return match_data.value <= max_value; + } + + static scoped_ptr<FulfillableCondition> Create( + const Extension* extension, + URLMatcherConditionFactory* url_matcher_condition_factory, + const base::Value& condition, + std::string* error) { + scoped_ptr<FulfillableCondition> result(new FulfillableCondition()); + const base::DictionaryValue* dict; + if (!condition.GetAsDictionary(&dict)) { + *error = "Expected dict"; + return result; + } + if (!dict->GetInteger("url_id", &result->condition_set_id)) + result->condition_set_id = -1; + if (!dict->GetInteger("max", &result->max_value)) + *error = "Expected integer at ['max']"; + if (result->condition_set_id != -1) { + result->condition_set = new URLMatcherConditionSet( + result->condition_set_id, + URLMatcherConditionSet::Conditions()); + } + return result; + } +}; + +TEST(DeclarativeConditionTest, FulfillConditionSet) { + typedef DeclarativeConditionSet<FulfillableCondition> FulfillableConditionSet; + FulfillableConditionSet::Values conditions; + conditions.push_back(ParseJson("{\"url_id\": 1, \"max\": 3}")); + conditions.push_back(ParseJson("{\"url_id\": 2, \"max\": 5}")); + conditions.push_back(ParseJson("{\"url_id\": 3, \"max\": 1}")); + conditions.push_back(ParseJson("{\"max\": -5}")); // No url. + + // Test insertion + std::string error; + scoped_ptr<FulfillableConditionSet> result = + FulfillableConditionSet::Create(NULL, NULL, conditions, &error); + ASSERT_EQ("", error); + ASSERT_TRUE(result); + EXPECT_EQ(4u, result->conditions().size()); + + std::set<URLMatcherConditionSet::ID> url_matches; + FulfillableCondition::MatchData match_data = { 0, url_matches }; + EXPECT_FALSE(result->IsFulfilled(1, match_data)) + << "Testing an ID that's not in url_matches forwards to the Condition, " + << "which doesn't match."; + EXPECT_FALSE(result->IsFulfilled(-1, match_data)) + << "Testing the 'no ID' value tries to match the 4th condition, but " + << "its max is too low."; + match_data.value = -5; + EXPECT_TRUE(result->IsFulfilled(-1, match_data)) + << "Testing the 'no ID' value tries to match the 4th condition, and " + << "this value is low enough."; + + url_matches.insert(1); + match_data.value = 3; + EXPECT_TRUE(result->IsFulfilled(1, match_data)) + << "Tests a condition with a url matcher, for a matching value."; + match_data.value = 4; + EXPECT_FALSE(result->IsFulfilled(1, match_data)) + << "Tests a condition with a url matcher, for a non-matching value " + << "that would match a different condition."; + url_matches.insert(2); + EXPECT_TRUE(result->IsFulfilled(2, match_data)) + << "Tests with 2 elements in the match set."; + + // Check the condition sets: + URLMatcherConditionSet::Vector condition_sets; + result->GetURLMatcherConditionSets(&condition_sets); + ASSERT_EQ(3U, condition_sets.size()); + EXPECT_EQ(1, condition_sets[0]->id()); + EXPECT_EQ(2, condition_sets[1]->id()); + EXPECT_EQ(3, condition_sets[2]->id()); +} + +// DeclarativeAction + +class SummingAction : public base::RefCounted<SummingAction> { + public: + typedef int ApplyInfo; + + SummingAction(int increment, int min_priority) + : increment_(increment), min_priority_(min_priority) {} + + static scoped_refptr<const SummingAction> Create( + content::BrowserContext* browser_context, + const Extension* extension, + const base::Value& action, + std::string* error, + bool* bad_message) { + int increment = 0; + int min_priority = 0; + const base::DictionaryValue* dict = NULL; + EXPECT_TRUE(action.GetAsDictionary(&dict)); + if (dict->HasKey("error")) { + EXPECT_TRUE(dict->GetString("error", error)); + return scoped_refptr<const SummingAction>(NULL); + } + if (dict->HasKey("bad")) { + *bad_message = true; + return scoped_refptr<const SummingAction>(NULL); + } + + EXPECT_TRUE(dict->GetInteger("value", &increment)); + dict->GetInteger("priority", &min_priority); + return scoped_refptr<const SummingAction>( + new SummingAction(increment, min_priority)); + } + + void Apply(const std::string& extension_id, + const base::Time& install_time, + int* sum) const { + *sum += increment_; + } + + int increment() const { return increment_; } + int minimum_priority() const { + return min_priority_; + } + + private: + friend class base::RefCounted<SummingAction>; + virtual ~SummingAction() {} + + int increment_; + int min_priority_; +}; +typedef DeclarativeActionSet<SummingAction> SummingActionSet; + +TEST(DeclarativeActionTest, ErrorActionSet) { + SummingActionSet::Values actions; + actions.push_back(ParseJson("{\"value\": 1}")); + actions.push_back(ParseJson("{\"error\": \"the error\"}")); + + std::string error; + bool bad = false; + scoped_ptr<SummingActionSet> result = + SummingActionSet::Create(NULL, NULL, actions, &error, &bad); + EXPECT_EQ("the error", error); + EXPECT_FALSE(bad); + EXPECT_FALSE(result); + + actions.clear(); + actions.push_back(ParseJson("{\"value\": 1}")); + actions.push_back(ParseJson("{\"bad\": 3}")); + result = SummingActionSet::Create(NULL, NULL, actions, &error, &bad); + EXPECT_EQ("", error); + EXPECT_TRUE(bad); + EXPECT_FALSE(result); +} + +TEST(DeclarativeActionTest, ApplyActionSet) { + SummingActionSet::Values actions; + actions.push_back( + ParseJson("{\"value\": 1," + " \"priority\": 5}")); + actions.push_back(ParseJson("{\"value\": 2}")); + + // Test insertion + std::string error; + bool bad = false; + scoped_ptr<SummingActionSet> result = + SummingActionSet::Create(NULL, NULL, actions, &error, &bad); + EXPECT_EQ("", error); + EXPECT_FALSE(bad); + ASSERT_TRUE(result); + EXPECT_EQ(2u, result->actions().size()); + + int sum = 0; + result->Apply("ext_id", base::Time(), &sum); + EXPECT_EQ(3, sum); + EXPECT_EQ(5, result->GetMinimumPriority()); +} + +TEST(DeclarativeRuleTest, Create) { + typedef DeclarativeRule<FulfillableCondition, SummingAction> Rule; + linked_ptr<Rule::JsonRule> json_rule(new Rule::JsonRule); + ASSERT_TRUE(Rule::JsonRule::Populate( + *ParseJson("{ \n" + " \"id\": \"rule1\", \n" + " \"conditions\": [ \n" + " {\"url_id\": 1, \"max\": 3}, \n" + " {\"url_id\": 2, \"max\": 5}, \n" + " ], \n" + " \"actions\": [ \n" + " { \n" + " \"value\": 2 \n" + " } \n" + " ], \n" + " \"priority\": 200 \n" + "}"), + json_rule.get())); + + const char kExtensionId[] = "ext1"; + scoped_refptr<Extension> extension = ExtensionBuilder() + .SetManifest(SimpleManifest()) + .SetID(kExtensionId) + .Build(); + + base::Time install_time = base::Time::Now(); + + URLMatcher matcher; + std::string error; + scoped_ptr<Rule> rule(Rule::Create(matcher.condition_factory(), + NULL, + extension.get(), + install_time, + json_rule, + Rule::ConsistencyChecker(), + &error)); + EXPECT_EQ("", error); + ASSERT_TRUE(rule.get()); + + EXPECT_EQ(kExtensionId, rule->id().first); + EXPECT_EQ("rule1", rule->id().second); + + EXPECT_EQ(200, rule->priority()); + + const Rule::ConditionSet& condition_set = rule->conditions(); + const Rule::ConditionSet::Conditions& conditions = + condition_set.conditions(); + ASSERT_EQ(2u, conditions.size()); + EXPECT_EQ(3, conditions[0]->max_value); + EXPECT_EQ(5, conditions[1]->max_value); + + const Rule::ActionSet& action_set = rule->actions(); + const Rule::ActionSet::Actions& actions = action_set.actions(); + ASSERT_EQ(1u, actions.size()); + EXPECT_EQ(2, actions[0]->increment()); + + int sum = 0; + rule->Apply(&sum); + EXPECT_EQ(2, sum); +} + +bool AtLeastOneCondition( + const DeclarativeConditionSet<FulfillableCondition>* conditions, + const DeclarativeActionSet<SummingAction>* actions, + std::string* error) { + if (conditions->conditions().empty()) { + *error = "No conditions"; + return false; + } + return true; +} + +TEST(DeclarativeRuleTest, CheckConsistency) { + typedef DeclarativeRule<FulfillableCondition, SummingAction> Rule; + URLMatcher matcher; + std::string error; + linked_ptr<Rule::JsonRule> json_rule(new Rule::JsonRule); + const char kExtensionId[] = "ext1"; + scoped_refptr<Extension> extension = ExtensionBuilder() + .SetManifest(SimpleManifest()) + .SetID(kExtensionId) + .Build(); + + ASSERT_TRUE(Rule::JsonRule::Populate( + *ParseJson("{ \n" + " \"id\": \"rule1\", \n" + " \"conditions\": [ \n" + " {\"url_id\": 1, \"max\": 3}, \n" + " {\"url_id\": 2, \"max\": 5}, \n" + " ], \n" + " \"actions\": [ \n" + " { \n" + " \"value\": 2 \n" + " } \n" + " ], \n" + " \"priority\": 200 \n" + "}"), + json_rule.get())); + scoped_ptr<Rule> rule(Rule::Create(matcher.condition_factory(), + NULL, + extension.get(), + base::Time(), + json_rule, + base::Bind(AtLeastOneCondition), + &error)); + EXPECT_TRUE(rule); + EXPECT_EQ("", error); + + ASSERT_TRUE(Rule::JsonRule::Populate( + *ParseJson("{ \n" + " \"id\": \"rule1\", \n" + " \"conditions\": [ \n" + " ], \n" + " \"actions\": [ \n" + " { \n" + " \"value\": 2 \n" + " } \n" + " ], \n" + " \"priority\": 200 \n" + "}"), + json_rule.get())); + rule = Rule::Create(matcher.condition_factory(), + NULL, + extension.get(), + base::Time(), + json_rule, + base::Bind(AtLeastOneCondition), + &error); + EXPECT_FALSE(rule); + EXPECT_EQ("No conditions", error); +} + +} // namespace extensions |