1// Copyright (c) 2012 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/deduping_factory.h"
6
7#include "base/values.h"
8#include "testing/gtest/include/gtest/gtest.h"
9
10namespace {
11
12const char kTypeName[] = "Foo";
13const char kTypeName2[] = "Foo2";
14
15// This serves as an example how to use the DedupingFactory.
16class BaseClass : public base::RefCounted<BaseClass> {
17 public:
18  // The type is introduced so that we can compare derived classes even though
19  // Equals takes a parameter of type BaseClass. Each derived class gets an
20  // entry in Type.
21  enum Type { FOO };
22
23  explicit BaseClass(Type type) : type_(type) {}
24
25  Type type() const { return type_; }
26
27  // For BaseClassT template:
28  virtual bool Equals(const BaseClass* other) const = 0;
29
30 protected:
31  friend class base::RefCounted<BaseClass>;
32  virtual ~BaseClass() {}
33
34 private:
35  const Type type_;
36};
37
38class Foo : public BaseClass {
39 public:
40  explicit Foo(int parameter) : BaseClass(FOO), parameter_(parameter) {}
41  virtual bool Equals(const BaseClass* other) const OVERRIDE {
42    return other->type() == type() &&
43           static_cast<const Foo*>(other)->parameter_ == parameter_;
44  }
45  int parameter() const {
46    return parameter_;
47  }
48
49 private:
50  friend class base::RefCounted<BaseClass>;
51  virtual ~Foo() {}
52
53  // Note that this class must be immutable.
54  const int parameter_;
55  DISALLOW_COPY_AND_ASSIGN(Foo);
56};
57
58scoped_refptr<const BaseClass> CreateFoo(const std::string& /*instance_type*/,
59                                         const base::Value* value,
60                                         std::string* error,
61                                         bool* bad_message) {
62  const base::DictionaryValue* dict = NULL;
63  CHECK(value->GetAsDictionary(&dict));
64  int parameter = 0;
65  if (!dict->GetInteger("parameter", &parameter)) {
66    *error = "No parameter";
67    *bad_message = true;
68    return scoped_refptr<const BaseClass>(NULL);
69  }
70  return scoped_refptr<const BaseClass>(new Foo(parameter));
71}
72
73scoped_ptr<base::DictionaryValue> CreateDictWithParameter(int parameter) {
74  scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
75  dict->SetInteger("parameter", parameter);
76  return dict.Pass();
77}
78
79}  // namespace
80
81namespace extensions {
82
83TEST(DedupingFactoryTest, InstantiationParameterized) {
84  DedupingFactory<BaseClass> factory(2);
85  factory.RegisterFactoryMethod(
86      kTypeName, DedupingFactory<BaseClass>::IS_PARAMETERIZED, &CreateFoo);
87
88  scoped_ptr<base::DictionaryValue> d1(CreateDictWithParameter(1));
89  scoped_ptr<base::DictionaryValue> d2(CreateDictWithParameter(2));
90  scoped_ptr<base::DictionaryValue> d3(CreateDictWithParameter(3));
91  scoped_ptr<base::DictionaryValue> d4(CreateDictWithParameter(4));
92
93  std::string error;
94  bool bad_message = false;
95
96  // Fill factory with 2 different types.
97  scoped_refptr<const BaseClass> c1(
98      factory.Instantiate(kTypeName, d1.get(), &error, &bad_message));
99  scoped_refptr<const BaseClass> c2(
100      factory.Instantiate(kTypeName, d2.get(), &error, &bad_message));
101  ASSERT_TRUE(c1.get());
102  ASSERT_TRUE(c2.get());
103  EXPECT_EQ(1, static_cast<const Foo*>(c1.get())->parameter());
104  EXPECT_EQ(2, static_cast<const Foo*>(c2.get())->parameter());
105
106  // This one produces an overflow, now the cache contains [2, 3]
107  scoped_refptr<const BaseClass> c3(
108      factory.Instantiate(kTypeName, d3.get(), &error, &bad_message));
109  ASSERT_TRUE(c3.get());
110  EXPECT_EQ(3, static_cast<const Foo*>(c3.get())->parameter());
111
112  // Reuse 2, this should give the same instance as c2.
113  scoped_refptr<const BaseClass> c2_b(
114      factory.Instantiate(kTypeName, d2.get(), &error, &bad_message));
115  EXPECT_EQ(2, static_cast<const Foo*>(c2_b.get())->parameter());
116  EXPECT_EQ(c2, c2_b);
117
118  // Also check that the reuse of 2 moved it to the end, so that the cache is
119  // now [3, 2] and 3 is discarded before 2.
120  // This discards 3, so the cache becomes [2, 1]
121  scoped_refptr<const BaseClass> c1_b(
122      factory.Instantiate(kTypeName, d1.get(), &error, &bad_message));
123
124  scoped_refptr<const BaseClass> c2_c(
125      factory.Instantiate(kTypeName, d2.get(), &error, &bad_message));
126  EXPECT_EQ(2, static_cast<const Foo*>(c2_c.get())->parameter());
127  EXPECT_EQ(c2, c2_c);
128}
129
130TEST(DedupingFactoryTest, InstantiationNonParameterized) {
131  DedupingFactory<BaseClass> factory(2);
132  factory.RegisterFactoryMethod(
133      kTypeName, DedupingFactory<BaseClass>::IS_NOT_PARAMETERIZED, &CreateFoo);
134
135  scoped_ptr<base::DictionaryValue> d1(CreateDictWithParameter(1));
136  scoped_ptr<base::DictionaryValue> d2(CreateDictWithParameter(2));
137
138  std::string error;
139  bool bad_message = false;
140
141  // We create two instances with different dictionaries but because the type is
142  // declared to be not parameterized, we should get the same instance.
143  scoped_refptr<const BaseClass> c1(
144      factory.Instantiate(kTypeName, d1.get(), &error, &bad_message));
145  scoped_refptr<const BaseClass> c2(
146      factory.Instantiate(kTypeName, d2.get(), &error, &bad_message));
147  ASSERT_TRUE(c1.get());
148  ASSERT_TRUE(c2.get());
149  EXPECT_EQ(1, static_cast<const Foo*>(c1.get())->parameter());
150  EXPECT_EQ(1, static_cast<const Foo*>(c2.get())->parameter());
151  EXPECT_EQ(c1, c2);
152}
153
154TEST(DedupingFactoryTest, TypeNames) {
155  DedupingFactory<BaseClass> factory(2);
156  factory.RegisterFactoryMethod(
157      kTypeName, DedupingFactory<BaseClass>::IS_PARAMETERIZED, &CreateFoo);
158  factory.RegisterFactoryMethod(
159      kTypeName2, DedupingFactory<BaseClass>::IS_PARAMETERIZED, &CreateFoo);
160
161  scoped_ptr<base::DictionaryValue> d1(CreateDictWithParameter(1));
162
163  std::string error;
164  bool bad_message = false;
165
166  scoped_refptr<const BaseClass> c1_a(
167      factory.Instantiate(kTypeName, d1.get(), &error, &bad_message));
168  scoped_refptr<const BaseClass> c1_b(
169      factory.Instantiate(kTypeName2, d1.get(), &error, &bad_message));
170
171  ASSERT_TRUE(c1_a.get());
172  ASSERT_TRUE(c1_b.get());
173  EXPECT_NE(c1_a, c1_b);
174}
175
176TEST(DedupingFactoryTest, Clear) {
177  DedupingFactory<BaseClass> factory(2);
178  factory.RegisterFactoryMethod(
179      kTypeName, DedupingFactory<BaseClass>::IS_PARAMETERIZED, &CreateFoo);
180
181  scoped_ptr<base::DictionaryValue> d1(CreateDictWithParameter(1));
182
183  std::string error;
184  bool bad_message = false;
185
186  scoped_refptr<const BaseClass> c1_a(
187      factory.Instantiate(kTypeName, d1.get(), &error, &bad_message));
188
189  factory.ClearPrototypes();
190
191  scoped_refptr<const BaseClass> c1_b(
192      factory.Instantiate(kTypeName, d1.get(), &error, &bad_message));
193
194  ASSERT_TRUE(c1_a.get());
195  ASSERT_TRUE(c1_b.get());
196  EXPECT_NE(c1_a, c1_b);
197}
198
199}  // namespace extensions
200