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