Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / extensions / browser / api / declarative / declarative_rule_unittest.cc
1 // Copyright (c) 2013 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 "extensions/browser/api/declarative/declarative_rule.h"
6
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/test/values_test_util.h"
10 #include "base/values.h"
11 #include "components/url_matcher/url_matcher_constants.h"
12 #include "extensions/common/extension_builder.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 using base::test::ParseJson;
17 using url_matcher::URLMatcher;
18 using url_matcher::URLMatcherConditionFactory;
19 using url_matcher::URLMatcherConditionSet;
20
21 namespace extensions {
22
23 namespace {
24
25 template<typename T>
26 linked_ptr<T> ScopedToLinkedPtr(scoped_ptr<T> ptr) {
27   return linked_ptr<T>(ptr.release());
28 }
29
30 scoped_ptr<base::DictionaryValue> SimpleManifest() {
31   return DictionaryBuilder()
32       .Set("name", "extension")
33       .Set("manifest_version", 2)
34       .Set("version", "1.0")
35       .Build();
36 }
37
38 }  // namespace
39
40 struct RecordingCondition {
41   typedef int MatchData;
42
43   URLMatcherConditionFactory* factory;
44   scoped_ptr<base::Value> value;
45
46   void GetURLMatcherConditionSets(
47       URLMatcherConditionSet::Vector* condition_sets) const {
48     // No condition sets.
49   }
50
51   static scoped_ptr<RecordingCondition> Create(
52       const Extension* extension,
53       URLMatcherConditionFactory* url_matcher_condition_factory,
54       const base::Value& condition,
55       std::string* error) {
56     const base::DictionaryValue* dict = NULL;
57     if (condition.GetAsDictionary(&dict) && dict->HasKey("bad_key")) {
58       *error = "Found error key";
59       return scoped_ptr<RecordingCondition>();
60     }
61
62     scoped_ptr<RecordingCondition> result(new RecordingCondition());
63     result->factory = url_matcher_condition_factory;
64     result->value.reset(condition.DeepCopy());
65     return result.Pass();
66   }
67 };
68 typedef DeclarativeConditionSet<RecordingCondition> RecordingConditionSet;
69
70 TEST(DeclarativeConditionTest, ErrorConditionSet) {
71   URLMatcher matcher;
72   RecordingConditionSet::AnyVector conditions;
73   conditions.push_back(ScopedToLinkedPtr(ParseJson("{\"key\": 1}")));
74   conditions.push_back(ScopedToLinkedPtr(ParseJson("{\"bad_key\": 2}")));
75
76   std::string error;
77   scoped_ptr<RecordingConditionSet> result = RecordingConditionSet::Create(
78       NULL, matcher.condition_factory(), conditions, &error);
79   EXPECT_EQ("Found error key", error);
80   ASSERT_FALSE(result);
81 }
82
83 TEST(DeclarativeConditionTest, CreateConditionSet) {
84   URLMatcher matcher;
85   RecordingConditionSet::AnyVector conditions;
86   conditions.push_back(ScopedToLinkedPtr(ParseJson("{\"key\": 1}")));
87   conditions.push_back(ScopedToLinkedPtr(ParseJson("[\"val1\", 2]")));
88
89   // Test insertion
90   std::string error;
91   scoped_ptr<RecordingConditionSet> result = RecordingConditionSet::Create(
92       NULL, matcher.condition_factory(), conditions, &error);
93   EXPECT_EQ("", error);
94   ASSERT_TRUE(result);
95   EXPECT_EQ(2u, result->conditions().size());
96
97   EXPECT_EQ(matcher.condition_factory(), result->conditions()[0]->factory);
98   EXPECT_TRUE(ParseJson("{\"key\": 1}")->Equals(
99       result->conditions()[0]->value.get()));
100 }
101
102 struct FulfillableCondition {
103   struct MatchData {
104     int value;
105     const std::set<URLMatcherConditionSet::ID>& url_matches;
106   };
107
108   scoped_refptr<URLMatcherConditionSet> condition_set;
109   int condition_set_id;
110   int max_value;
111
112   URLMatcherConditionSet::ID url_matcher_condition_set_id() const {
113     return condition_set_id;
114   }
115
116   scoped_refptr<URLMatcherConditionSet> url_matcher_condition_set() const {
117     return condition_set;
118   }
119
120   void GetURLMatcherConditionSets(
121       URLMatcherConditionSet::Vector* condition_sets) const {
122     if (condition_set.get())
123       condition_sets->push_back(condition_set);
124   }
125
126   bool IsFulfilled(const MatchData& match_data) const {
127     if (condition_set_id != -1 &&
128         !ContainsKey(match_data.url_matches, condition_set_id))
129       return false;
130     return match_data.value <= max_value;
131   }
132
133   static scoped_ptr<FulfillableCondition> Create(
134       const Extension* extension,
135       URLMatcherConditionFactory* url_matcher_condition_factory,
136       const base::Value& condition,
137       std::string* error) {
138     scoped_ptr<FulfillableCondition> result(new FulfillableCondition());
139     const base::DictionaryValue* dict;
140     if (!condition.GetAsDictionary(&dict)) {
141       *error = "Expected dict";
142       return result.Pass();
143     }
144     if (!dict->GetInteger("url_id", &result->condition_set_id))
145       result->condition_set_id = -1;
146     if (!dict->GetInteger("max", &result->max_value))
147       *error = "Expected integer at ['max']";
148     if (result->condition_set_id != -1) {
149       result->condition_set = new URLMatcherConditionSet(
150           result->condition_set_id,
151           URLMatcherConditionSet::Conditions());
152     }
153     return result.Pass();
154   }
155 };
156
157 TEST(DeclarativeConditionTest, FulfillConditionSet) {
158   typedef DeclarativeConditionSet<FulfillableCondition> FulfillableConditionSet;
159   FulfillableConditionSet::AnyVector conditions;
160   conditions.push_back(ScopedToLinkedPtr(ParseJson(
161       "{\"url_id\": 1, \"max\": 3}")));
162   conditions.push_back(ScopedToLinkedPtr(ParseJson(
163       "{\"url_id\": 2, \"max\": 5}")));
164   conditions.push_back(ScopedToLinkedPtr(ParseJson(
165       "{\"url_id\": 3, \"max\": 1}")));
166   conditions.push_back(ScopedToLinkedPtr(ParseJson(
167       "{\"max\": -5}")));  // No url.
168
169   // Test insertion
170   std::string error;
171   scoped_ptr<FulfillableConditionSet> result =
172       FulfillableConditionSet::Create(NULL, NULL, conditions, &error);
173   ASSERT_EQ("", error);
174   ASSERT_TRUE(result);
175   EXPECT_EQ(4u, result->conditions().size());
176
177   std::set<URLMatcherConditionSet::ID> url_matches;
178   FulfillableCondition::MatchData match_data = { 0, url_matches };
179   EXPECT_FALSE(result->IsFulfilled(1, match_data))
180       << "Testing an ID that's not in url_matches forwards to the Condition, "
181       << "which doesn't match.";
182   EXPECT_FALSE(result->IsFulfilled(-1, match_data))
183       << "Testing the 'no ID' value tries to match the 4th condition, but "
184       << "its max is too low.";
185   match_data.value = -5;
186   EXPECT_TRUE(result->IsFulfilled(-1, match_data))
187       << "Testing the 'no ID' value tries to match the 4th condition, and "
188       << "this value is low enough.";
189
190   url_matches.insert(1);
191   match_data.value = 3;
192   EXPECT_TRUE(result->IsFulfilled(1, match_data))
193       << "Tests a condition with a url matcher, for a matching value.";
194   match_data.value = 4;
195   EXPECT_FALSE(result->IsFulfilled(1, match_data))
196       << "Tests a condition with a url matcher, for a non-matching value "
197       << "that would match a different condition.";
198   url_matches.insert(2);
199   EXPECT_TRUE(result->IsFulfilled(2, match_data))
200       << "Tests with 2 elements in the match set.";
201
202   // Check the condition sets:
203   URLMatcherConditionSet::Vector condition_sets;
204   result->GetURLMatcherConditionSets(&condition_sets);
205   ASSERT_EQ(3U, condition_sets.size());
206   EXPECT_EQ(1, condition_sets[0]->id());
207   EXPECT_EQ(2, condition_sets[1]->id());
208   EXPECT_EQ(3, condition_sets[2]->id());
209 }
210
211 // DeclarativeAction
212
213 class SummingAction : public base::RefCounted<SummingAction> {
214  public:
215   typedef int ApplyInfo;
216
217   SummingAction(int increment, int min_priority)
218       : increment_(increment), min_priority_(min_priority) {}
219
220   static scoped_refptr<const SummingAction> Create(
221       content::BrowserContext* browser_context,
222       const Extension* extension,
223       const base::Value& action,
224       std::string* error,
225       bool* bad_message) {
226     int increment = 0;
227     int min_priority = 0;
228     const base::DictionaryValue* dict = NULL;
229     EXPECT_TRUE(action.GetAsDictionary(&dict));
230     if (dict->HasKey("error")) {
231       EXPECT_TRUE(dict->GetString("error", error));
232       return scoped_refptr<const SummingAction>(NULL);
233     }
234     if (dict->HasKey("bad")) {
235       *bad_message = true;
236       return scoped_refptr<const SummingAction>(NULL);
237     }
238
239     EXPECT_TRUE(dict->GetInteger("value", &increment));
240     dict->GetInteger("priority", &min_priority);
241     return scoped_refptr<const SummingAction>(
242         new SummingAction(increment, min_priority));
243   }
244
245   void Apply(const std::string& extension_id,
246              const base::Time& install_time,
247              int* sum) const {
248     *sum += increment_;
249   }
250
251   int increment() const { return increment_; }
252   int minimum_priority() const {
253     return min_priority_;
254   }
255
256  private:
257   friend class base::RefCounted<SummingAction>;
258   virtual ~SummingAction() {}
259
260   int increment_;
261   int min_priority_;
262 };
263 typedef DeclarativeActionSet<SummingAction> SummingActionSet;
264
265 TEST(DeclarativeActionTest, ErrorActionSet) {
266   SummingActionSet::AnyVector actions;
267   actions.push_back(ScopedToLinkedPtr(ParseJson("{\"value\": 1}")));
268   actions.push_back(ScopedToLinkedPtr(ParseJson("{\"error\": \"the error\"}")));
269
270   std::string error;
271   bool bad = false;
272   scoped_ptr<SummingActionSet> result =
273       SummingActionSet::Create(NULL, NULL, actions, &error, &bad);
274   EXPECT_EQ("the error", error);
275   EXPECT_FALSE(bad);
276   EXPECT_FALSE(result);
277
278   actions.clear();
279   actions.push_back(ScopedToLinkedPtr(ParseJson("{\"value\": 1}")));
280   actions.push_back(ScopedToLinkedPtr(ParseJson("{\"bad\": 3}")));
281   result = SummingActionSet::Create(NULL, NULL, actions, &error, &bad);
282   EXPECT_EQ("", error);
283   EXPECT_TRUE(bad);
284   EXPECT_FALSE(result);
285 }
286
287 TEST(DeclarativeActionTest, ApplyActionSet) {
288   SummingActionSet::AnyVector actions;
289   actions.push_back(ScopedToLinkedPtr(ParseJson(
290       "{\"value\": 1,"
291       " \"priority\": 5}")));
292   actions.push_back(ScopedToLinkedPtr(ParseJson("{\"value\": 2}")));
293
294   // Test insertion
295   std::string error;
296   bool bad = false;
297   scoped_ptr<SummingActionSet> result =
298       SummingActionSet::Create(NULL, NULL, actions, &error, &bad);
299   EXPECT_EQ("", error);
300   EXPECT_FALSE(bad);
301   ASSERT_TRUE(result);
302   EXPECT_EQ(2u, result->actions().size());
303
304   int sum = 0;
305   result->Apply("ext_id", base::Time(), &sum);
306   EXPECT_EQ(3, sum);
307   EXPECT_EQ(5, result->GetMinimumPriority());
308 }
309
310 TEST(DeclarativeRuleTest, Create) {
311   typedef DeclarativeRule<FulfillableCondition, SummingAction> Rule;
312   linked_ptr<Rule::JsonRule> json_rule(new Rule::JsonRule);
313   ASSERT_TRUE(Rule::JsonRule::Populate(
314       *ParseJson("{ \n"
315                  "  \"id\": \"rule1\", \n"
316                  "  \"conditions\": [ \n"
317                  "    {\"url_id\": 1, \"max\": 3}, \n"
318                  "    {\"url_id\": 2, \"max\": 5}, \n"
319                  "  ], \n"
320                  "  \"actions\": [ \n"
321                  "    { \n"
322                  "      \"value\": 2 \n"
323                  "    } \n"
324                  "  ], \n"
325                  "  \"priority\": 200 \n"
326                  "}"),
327       json_rule.get()));
328
329   const char kExtensionId[] = "ext1";
330   scoped_refptr<Extension> extension = ExtensionBuilder()
331                                            .SetManifest(SimpleManifest())
332                                            .SetID(kExtensionId)
333                                            .Build();
334
335   base::Time install_time = base::Time::Now();
336
337   URLMatcher matcher;
338   std::string error;
339   scoped_ptr<Rule> rule(Rule::Create(matcher.condition_factory(),
340                                      NULL,
341                                      extension.get(),
342                                      install_time,
343                                      json_rule,
344                                      Rule::ConsistencyChecker(),
345                                      &error));
346   EXPECT_EQ("", error);
347   ASSERT_TRUE(rule.get());
348
349   EXPECT_EQ(kExtensionId, rule->id().first);
350   EXPECT_EQ("rule1", rule->id().second);
351
352   EXPECT_EQ(200, rule->priority());
353
354   const Rule::ConditionSet& condition_set = rule->conditions();
355   const Rule::ConditionSet::Conditions& conditions =
356       condition_set.conditions();
357   ASSERT_EQ(2u, conditions.size());
358   EXPECT_EQ(3, conditions[0]->max_value);
359   EXPECT_EQ(5, conditions[1]->max_value);
360
361   const Rule::ActionSet& action_set = rule->actions();
362   const Rule::ActionSet::Actions& actions = action_set.actions();
363   ASSERT_EQ(1u, actions.size());
364   EXPECT_EQ(2, actions[0]->increment());
365
366   int sum = 0;
367   rule->Apply(&sum);
368   EXPECT_EQ(2, sum);
369 }
370
371 bool AtLeastOneCondition(
372     const DeclarativeConditionSet<FulfillableCondition>* conditions,
373     const DeclarativeActionSet<SummingAction>* actions,
374     std::string* error) {
375   if (conditions->conditions().empty()) {
376     *error = "No conditions";
377     return false;
378   }
379   return true;
380 }
381
382 TEST(DeclarativeRuleTest, CheckConsistency) {
383   typedef DeclarativeRule<FulfillableCondition, SummingAction> Rule;
384   URLMatcher matcher;
385   std::string error;
386   linked_ptr<Rule::JsonRule> json_rule(new Rule::JsonRule);
387   const char kExtensionId[] = "ext1";
388   scoped_refptr<Extension> extension = ExtensionBuilder()
389                                            .SetManifest(SimpleManifest())
390                                            .SetID(kExtensionId)
391                                            .Build();
392
393   ASSERT_TRUE(Rule::JsonRule::Populate(
394       *ParseJson("{ \n"
395                  "  \"id\": \"rule1\", \n"
396                  "  \"conditions\": [ \n"
397                  "    {\"url_id\": 1, \"max\": 3}, \n"
398                  "    {\"url_id\": 2, \"max\": 5}, \n"
399                  "  ], \n"
400                  "  \"actions\": [ \n"
401                  "    { \n"
402                  "      \"value\": 2 \n"
403                  "    } \n"
404                  "  ], \n"
405                  "  \"priority\": 200 \n"
406                  "}"),
407       json_rule.get()));
408   scoped_ptr<Rule> rule(Rule::Create(matcher.condition_factory(),
409                                      NULL,
410                                      extension.get(),
411                                      base::Time(),
412                                      json_rule,
413                                      base::Bind(AtLeastOneCondition),
414                                      &error));
415   EXPECT_TRUE(rule);
416   EXPECT_EQ("", error);
417
418   ASSERT_TRUE(Rule::JsonRule::Populate(
419       *ParseJson("{ \n"
420                  "  \"id\": \"rule1\", \n"
421                  "  \"conditions\": [ \n"
422                  "  ], \n"
423                  "  \"actions\": [ \n"
424                  "    { \n"
425                  "      \"value\": 2 \n"
426                  "    } \n"
427                  "  ], \n"
428                  "  \"priority\": 200 \n"
429                  "}"),
430       json_rule.get()));
431   rule = Rule::Create(matcher.condition_factory(),
432                       NULL,
433                       extension.get(),
434                       base::Time(),
435                       json_rule,
436                       base::Bind(AtLeastOneCondition),
437                       &error);
438   EXPECT_FALSE(rule);
439   EXPECT_EQ("No conditions", error);
440 }
441
442 }  // namespace extensions