blob: 19cabb35a3c6d196ebf66b62dfe17790d8261728 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// 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/deduping_factory.h"
#include <memory>
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const char kTypeName[] = "Foo";
const char kTypeName2[] = "Foo2";
// This serves as an example how to use the DedupingFactory.
class BaseClass : public base::RefCounted<BaseClass> {
public:
// The type is introduced so that we can compare derived classes even though
// Equals takes a parameter of type BaseClass. Each derived class gets an
// entry in Type.
enum Type { FOO };
explicit BaseClass(Type type) : type_(type) {}
Type type() const { return type_; }
// For BaseClassT template:
virtual bool Equals(const BaseClass* other) const = 0;
protected:
friend class base::RefCounted<BaseClass>;
virtual ~BaseClass() {}
private:
const Type type_;
};
class Foo : public BaseClass {
public:
explicit Foo(int parameter) : BaseClass(FOO), parameter_(parameter) {}
Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;
bool Equals(const BaseClass* other) const override {
return other->type() == type() &&
static_cast<const Foo*>(other)->parameter_ == parameter_;
}
int parameter() const {
return parameter_;
}
private:
friend class base::RefCounted<BaseClass>;
~Foo() override {}
// Note that this class must be immutable.
const int parameter_;
};
scoped_refptr<const BaseClass> CreateFoo(const std::string& /*instance_type*/,
const base::Value::Dict& value,
std::string* error,
bool* bad_message) {
std::optional<int> parameter = value.FindInt("parameter");
if (!parameter) {
*error = "No parameter";
*bad_message = true;
return nullptr;
}
return scoped_refptr<const BaseClass>(new Foo(*parameter));
}
base::Value::Dict CreateDictWithParameter(int parameter) {
base::Value::Dict dict;
dict.Set("parameter", parameter);
return dict;
}
} // namespace
namespace extensions {
using FactoryT = DedupingFactory<BaseClass, const base::Value::Dict&>;
TEST(DedupingFactoryTest, InstantiationParameterized) {
FactoryT factory(2);
factory.RegisterFactoryMethod(kTypeName, FactoryT::IS_PARAMETERIZED,
&CreateFoo);
base::Value::Dict d1 = CreateDictWithParameter(1);
base::Value::Dict d2 = CreateDictWithParameter(2);
base::Value::Dict d3 = CreateDictWithParameter(3);
base::Value::Dict d4 = CreateDictWithParameter(4);
std::string error;
bool bad_message = false;
// Fill factory with 2 different types.
scoped_refptr<const BaseClass> c1(
factory.Instantiate(kTypeName, d1, &error, &bad_message));
scoped_refptr<const BaseClass> c2(
factory.Instantiate(kTypeName, d2, &error, &bad_message));
ASSERT_TRUE(c1.get());
ASSERT_TRUE(c2.get());
EXPECT_EQ(1, static_cast<const Foo*>(c1.get())->parameter());
EXPECT_EQ(2, static_cast<const Foo*>(c2.get())->parameter());
// This one produces an overflow, now the cache contains [2, 3]
scoped_refptr<const BaseClass> c3(
factory.Instantiate(kTypeName, d3, &error, &bad_message));
ASSERT_TRUE(c3.get());
EXPECT_EQ(3, static_cast<const Foo*>(c3.get())->parameter());
// Reuse 2, this should give the same instance as c2.
scoped_refptr<const BaseClass> c2_b(
factory.Instantiate(kTypeName, d2, &error, &bad_message));
EXPECT_EQ(2, static_cast<const Foo*>(c2_b.get())->parameter());
EXPECT_EQ(c2, c2_b);
// Also check that the reuse of 2 moved it to the end, so that the cache is
// now [3, 2] and 3 is discarded before 2.
// This discards 3, so the cache becomes [2, 1]
scoped_refptr<const BaseClass> c1_b(
factory.Instantiate(kTypeName, d1, &error, &bad_message));
scoped_refptr<const BaseClass> c2_c(
factory.Instantiate(kTypeName, d2, &error, &bad_message));
EXPECT_EQ(2, static_cast<const Foo*>(c2_c.get())->parameter());
EXPECT_EQ(c2, c2_c);
}
TEST(DedupingFactoryTest, InstantiationNonParameterized) {
FactoryT factory(2);
factory.RegisterFactoryMethod(kTypeName, FactoryT::IS_NOT_PARAMETERIZED,
&CreateFoo);
base::Value::Dict d1 = CreateDictWithParameter(1);
base::Value::Dict d2 = CreateDictWithParameter(2);
std::string error;
bool bad_message = false;
// We create two instances with different dictionaries but because the type is
// declared to be not parameterized, we should get the same instance.
scoped_refptr<const BaseClass> c1(
factory.Instantiate(kTypeName, d1, &error, &bad_message));
scoped_refptr<const BaseClass> c2(
factory.Instantiate(kTypeName, d2, &error, &bad_message));
ASSERT_TRUE(c1.get());
ASSERT_TRUE(c2.get());
EXPECT_EQ(1, static_cast<const Foo*>(c1.get())->parameter());
EXPECT_EQ(1, static_cast<const Foo*>(c2.get())->parameter());
EXPECT_EQ(c1, c2);
}
TEST(DedupingFactoryTest, TypeNames) {
FactoryT factory(2);
factory.RegisterFactoryMethod(kTypeName, FactoryT::IS_PARAMETERIZED,
&CreateFoo);
factory.RegisterFactoryMethod(kTypeName2, FactoryT::IS_PARAMETERIZED,
&CreateFoo);
base::Value::Dict d1 = CreateDictWithParameter(1);
std::string error;
bool bad_message = false;
scoped_refptr<const BaseClass> c1_a(
factory.Instantiate(kTypeName, d1, &error, &bad_message));
scoped_refptr<const BaseClass> c1_b(
factory.Instantiate(kTypeName2, d1, &error, &bad_message));
ASSERT_TRUE(c1_a.get());
ASSERT_TRUE(c1_b.get());
EXPECT_NE(c1_a, c1_b);
}
TEST(DedupingFactoryTest, Clear) {
FactoryT factory(2);
factory.RegisterFactoryMethod(kTypeName, FactoryT::IS_PARAMETERIZED,
&CreateFoo);
base::Value::Dict d1 = CreateDictWithParameter(1);
std::string error;
bool bad_message = false;
scoped_refptr<const BaseClass> c1_a(
factory.Instantiate(kTypeName, d1, &error, &bad_message));
factory.ClearPrototypes();
scoped_refptr<const BaseClass> c1_b(
factory.Instantiate(kTypeName, d1, &error, &bad_message));
ASSERT_TRUE(c1_a.get());
ASSERT_TRUE(c1_b.get());
EXPECT_NE(c1_a, c1_b);
}
} // namespace extensions