| Index: chromecast/base/device_capabilities_impl_unittest.cc
|
| diff --git a/chromecast/base/device_capabilities_impl_unittest.cc b/chromecast/base/device_capabilities_impl_unittest.cc
|
| index eab7a9ec19337d6dec30d52cc8e0f10af618fa43..ce676df8fd8203be9008d1d8a3d5cb7f2bfaabce 100644
|
| --- a/chromecast/base/device_capabilities_impl_unittest.cc
|
| +++ b/chromecast/base/device_capabilities_impl_unittest.cc
|
| @@ -6,6 +6,8 @@
|
|
|
| #include <string>
|
|
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/run_loop.h"
|
| #include "base/values.h"
|
| #include "chromecast/base/serializers.h"
|
| #include "testing/gmock/include/gmock/gmock.h"
|
| @@ -21,6 +23,10 @@ const char kSampleDictionaryCapability[] =
|
| " \"dummy_field_int\": 99"
|
| "}";
|
|
|
| +void GetSampleDefaultCapability(std::string* key,
|
| + scoped_ptr<base::Value>* init_value);
|
| +void TestBasicOperations(DeviceCapabilities* capabilities);
|
| +
|
| // Simple capability manager that implements the Validator interface. Either
|
| // accepts or rejects all proposed changes based on |accept_changes| constructor
|
| // argument.
|
| @@ -58,6 +64,59 @@ class FakeCapabilityManagerSimple : public DeviceCapabilities::Validator {
|
| const bool accept_changes_;
|
| };
|
|
|
| +// Used to test that capabilities/validator can be read and written in
|
| +// Validate() without encountering deadlocks/unexpected behavior.
|
| +class FakeCapabilityManagerComplex : public DeviceCapabilities::Validator {
|
| + public:
|
| + FakeCapabilityManagerComplex(DeviceCapabilities* capabilities,
|
| + const std::string& key)
|
| + : DeviceCapabilities::Validator(capabilities), key_(key) {
|
| + capabilities->Register(key, this);
|
| + }
|
| +
|
| + // Unregisters itself as Validator.
|
| + ~FakeCapabilityManagerComplex() override {
|
| + capabilities()->Unregister(key_, this);
|
| + }
|
| +
|
| + // Runs TestBasicOperations().
|
| + void Validate(const std::string& path,
|
| + scoped_ptr<base::Value> proposed_value) override {
|
| + TestBasicOperations(capabilities());
|
| + }
|
| +
|
| + private:
|
| + const std::string key_;
|
| +};
|
| +
|
| +// Used to test that capabilities/validators can be read and written in
|
| +// OnCapabilitiesChanged() without encountering deadlocks/unexpected behavior.
|
| +class FakeCapabilitiesObserver : public DeviceCapabilities::Observer {
|
| + public:
|
| + FakeCapabilitiesObserver(DeviceCapabilities* capabilities)
|
| + : capabilities_(capabilities), removed_as_observer(false) {
|
| + capabilities_->AddCapabilitiesObserver(this);
|
| + }
|
| +
|
| + ~FakeCapabilitiesObserver() override {
|
| + if (!removed_as_observer)
|
| + capabilities_->RemoveCapabilitiesObserver(this);
|
| + }
|
| +
|
| + // Runs TestBasicOperations().
|
| + void OnCapabilitiesChanged(const std::string& path) override {
|
| + TestBasicOperations(capabilities_);
|
| + // To prevent infinite loop of SetCapability() -> OnCapabilitiesChanged()
|
| + // -> SetCapability() -> OnCapabilitiesChanged() etc.
|
| + capabilities_->RemoveCapabilitiesObserver(this);
|
| + removed_as_observer = true;
|
| + }
|
| +
|
| + private:
|
| + DeviceCapabilities* const capabilities_;
|
| + bool removed_as_observer;
|
| +};
|
| +
|
| // Used to test that OnCapabilitiesChanged() is called when capabilities are
|
| // modified
|
| class MockCapabilitiesObserver : public DeviceCapabilities::Observer {
|
| @@ -116,11 +175,55 @@ bool JsonStringEquals(const std::string& json,
|
| return dict_json.get() && *dict_json == json;
|
| }
|
|
|
| +// The function runs through the set of basic operations of DeviceCapabilities.
|
| +// Register validator for sample default capability, reads capability, writes
|
| +// capability, and unregister validator. After it has completed, use
|
| +// AssertBasicOperationsSuccessful() to ensure that all operations completed
|
| +// successfully. Sample default capability should not be added or registered in
|
| +// class before this function is called.
|
| +void TestBasicOperations(DeviceCapabilities* capabilities) {
|
| + std::string key;
|
| + scoped_ptr<base::Value> init_value;
|
| + GetSampleDefaultCapability(&key, &init_value);
|
| +
|
| + ASSERT_EQ(capabilities->GetCapability(key), nullptr);
|
| + ASSERT_EQ(capabilities->GetValidator(key), nullptr);
|
| +
|
| + // Register and write capability
|
| + FakeCapabilityManagerSimple* manager(new FakeCapabilityManagerSimple(
|
| + capabilities, key, init_value->CreateDeepCopy(), true));
|
| + // Read Validator
|
| + EXPECT_EQ(capabilities->GetValidator(key), manager);
|
| + // Read Capability
|
| + EXPECT_TRUE(base::Value::Equals(capabilities->GetCapability(key).get(),
|
| + init_value.get()));
|
| + // Unregister
|
| + delete manager;
|
| +
|
| + // Write capability again. Provides way of checking that this function
|
| + // ran and was successful.
|
| + scoped_ptr<base::Value> new_value = GetSampleDefaultCapabilityNewValue();
|
| + capabilities->SetCapability(key, new_value.Pass());
|
| +}
|
| +
|
| +// See TestBasicOperations() comment.
|
| +void AssertBasicOperationsSuccessful(const DeviceCapabilities* capabilities) {
|
| + base::RunLoop().RunUntilIdle();
|
| + std::string key;
|
| + scoped_ptr<base::Value> init_value;
|
| + GetSampleDefaultCapability(&key, &init_value);
|
| + scoped_ptr<base::Value> value = capabilities->GetCapability(key);
|
| + ASSERT_NE(value, nullptr);
|
| + scoped_ptr<base::Value> new_value = GetSampleDefaultCapabilityNewValue();
|
| + EXPECT_TRUE(base::Value::Equals(value.get(), new_value.get()));
|
| +}
|
| +
|
| } // namespace
|
|
|
| class DeviceCapabilitiesImplTest : public ::testing::Test {
|
| protected:
|
| void SetUp() override {
|
| + message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_IO));
|
| capabilities_ = DeviceCapabilities::Create();
|
| mock_capabilities_observer_.reset(new MockCapabilitiesObserver());
|
| capabilities_->AddCapabilitiesObserver(mock_capabilities_observer_.get());
|
| @@ -136,6 +239,9 @@ class DeviceCapabilitiesImplTest : public ::testing::Test {
|
| void TearDown() override {
|
| capabilities_->RemoveCapabilitiesObserver(
|
| mock_capabilities_observer_.get());
|
| + mock_capabilities_observer_.reset();
|
| + capabilities_.reset();
|
| + message_loop_.reset();
|
| }
|
|
|
| DeviceCapabilities* capabilities() const { return capabilities_.get(); }
|
| @@ -145,6 +251,7 @@ class DeviceCapabilitiesImplTest : public ::testing::Test {
|
| }
|
|
|
| private:
|
| + scoped_ptr<base::MessageLoop> message_loop_;
|
| scoped_ptr<DeviceCapabilities> capabilities_;
|
| scoped_ptr<MockCapabilitiesObserver> mock_capabilities_observer_;
|
| };
|
| @@ -170,8 +277,7 @@ TEST_F(DeviceCapabilitiesImplTest, Register) {
|
| scoped_ptr<const std::string> empty_dict_string(
|
| SerializeToJson(base::DictionaryValue()));
|
| EXPECT_EQ(capabilities()->GetCapabilitiesString(), *empty_dict_string);
|
| - const base::Value* value = nullptr;
|
| - EXPECT_FALSE(capabilities()->GetCapability(key, &value));
|
| + EXPECT_EQ(capabilities()->GetCapability(key), nullptr);
|
| }
|
|
|
| // Tests Unregister() of a default capability.
|
| @@ -190,8 +296,7 @@ TEST_F(DeviceCapabilitiesImplTest, Unregister) {
|
| scoped_ptr<const std::string> empty_dict_string(
|
| SerializeToJson(base::DictionaryValue()));
|
| EXPECT_EQ(capabilities()->GetCapabilitiesString(), *empty_dict_string);
|
| - const base::Value* value = nullptr;
|
| - EXPECT_FALSE(capabilities()->GetCapability(key, &value));
|
| + EXPECT_EQ(capabilities()->GetCapability(key), nullptr);
|
| }
|
|
|
| // Tests GetCapability() and updating the value through SetCapability().
|
| @@ -202,15 +307,14 @@ TEST_F(DeviceCapabilitiesImplTest, GetCapabilityAndSetCapability) {
|
| FakeCapabilityManagerSimple manager(capabilities(), key,
|
| init_value->CreateDeepCopy(), true);
|
|
|
| - const base::Value* value = nullptr;
|
| - EXPECT_TRUE(capabilities()->GetCapability(key, &value));
|
| - EXPECT_TRUE(base::Value::Equals(value, init_value.get()));
|
| + EXPECT_TRUE(base::Value::Equals(capabilities()->GetCapability(key).get(),
|
| + init_value.get()));
|
|
|
| scoped_ptr<base::Value> new_value = GetSampleDefaultCapabilityNewValue();
|
| capabilities()->SetCapability(key, new_value->CreateDeepCopy());
|
| - value = nullptr;
|
| - EXPECT_TRUE(capabilities()->GetCapability(key, &value));
|
| - EXPECT_TRUE(base::Value::Equals(value, new_value.get()));
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_TRUE(base::Value::Equals(capabilities()->GetCapability(key).get(),
|
| + new_value.get()));
|
| }
|
|
|
| // Tests BluetoothSupported() and updating this value through SetCapability().
|
| @@ -223,6 +327,7 @@ TEST_F(DeviceCapabilitiesImplTest, BluetoothSupportedAndSetCapability) {
|
| capabilities()->SetCapability(
|
| DeviceCapabilities::kKeyBluetoothSupported,
|
| make_scoped_ptr(new base::FundamentalValue(false)));
|
| + base::RunLoop().RunUntilIdle();
|
| EXPECT_FALSE(capabilities()->BluetoothSupported());
|
| }
|
|
|
| @@ -236,6 +341,7 @@ TEST_F(DeviceCapabilitiesImplTest, DisplaySupportedAndSetCapability) {
|
| capabilities()->SetCapability(
|
| DeviceCapabilities::kKeyDisplaySupported,
|
| make_scoped_ptr(new base::FundamentalValue(false)));
|
| + base::RunLoop().RunUntilIdle();
|
| EXPECT_FALSE(capabilities()->DisplaySupported());
|
| }
|
|
|
| @@ -249,10 +355,10 @@ TEST_F(DeviceCapabilitiesImplTest, SetCapabilityInvalid) {
|
| init_value->CreateDeepCopy(), false);
|
|
|
| capabilities()->SetCapability(key, GetSampleDefaultCapabilityNewValue());
|
| + base::RunLoop().RunUntilIdle();
|
|
|
| - const base::Value* value = nullptr;
|
| - EXPECT_TRUE(capabilities()->GetCapability(key, &value));
|
| - EXPECT_TRUE(base::Value::Equals(init_value.get(), value));
|
| + EXPECT_TRUE(base::Value::Equals(capabilities()->GetCapability(key).get(),
|
| + init_value.get()));
|
| }
|
|
|
| // Test that SetCapability() updates the capabilities string correctly
|
| @@ -268,6 +374,7 @@ TEST_F(DeviceCapabilitiesImplTest, SetCapabilityUpdatesString) {
|
|
|
| scoped_ptr<base::Value> new_value = GetSampleDefaultCapabilityNewValue();
|
| capabilities()->SetCapability(key, new_value->CreateDeepCopy());
|
| + base::RunLoop().RunUntilIdle();
|
| EXPECT_TRUE(JsonStringEquals(capabilities()->GetCapabilitiesString(), key,
|
| *new_value));
|
| }
|
| @@ -293,6 +400,7 @@ TEST_F(DeviceCapabilitiesImplTest, SetCapabilityNotifiesObservers) {
|
|
|
| // 3rd call
|
| capabilities()->SetCapability(key, init_value.Pass());
|
| + base::RunLoop().RunUntilIdle();
|
| }
|
|
|
| // Test adding dynamic capabilities
|
| @@ -301,20 +409,20 @@ TEST_F(DeviceCapabilitiesImplTest, SetCapabilityDynamic) {
|
| scoped_ptr<base::Value> init_value;
|
| GetSampleDynamicCapability(&key, &init_value);
|
|
|
| - const base::Value* value = nullptr;
|
| - ASSERT_FALSE(capabilities()->GetCapability(key, &value));
|
| + ASSERT_EQ(capabilities()->GetCapability(key), nullptr);
|
| capabilities()->SetCapability(key, init_value->CreateDeepCopy());
|
| + base::RunLoop().RunUntilIdle();
|
|
|
| - EXPECT_TRUE(capabilities()->GetCapability(key, &value));
|
| - EXPECT_TRUE(base::Value::Equals(init_value.get(), value));
|
| + EXPECT_TRUE(base::Value::Equals(capabilities()->GetCapability(key).get(),
|
| + init_value.get()));
|
| EXPECT_TRUE(JsonStringEquals(capabilities()->GetCapabilitiesString(), key,
|
| *init_value));
|
|
|
| scoped_ptr<base::Value> new_value = GetSampleDynamicCapabilityNewValue();
|
| capabilities()->SetCapability(key, new_value->CreateDeepCopy());
|
| - value = nullptr;
|
| - EXPECT_TRUE(capabilities()->GetCapability(key, &value));
|
| - EXPECT_TRUE(base::Value::Equals(new_value.get(), value));
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_TRUE(base::Value::Equals(capabilities()->GetCapability(key).get(),
|
| + new_value.get()));
|
| EXPECT_TRUE(JsonStringEquals(capabilities()->GetCapabilitiesString(), key,
|
| *new_value));
|
| }
|
| @@ -332,20 +440,21 @@ TEST_F(DeviceCapabilitiesImplTest, SetCapabilityDictionary) {
|
| capabilities()->SetCapability(
|
| "dummy_dictionary_key.dummy_field_bool",
|
| make_scoped_ptr(new base::FundamentalValue(false)));
|
| - const base::Value* value = nullptr;
|
| + base::RunLoop().RunUntilIdle();
|
| bool value_bool = true;
|
| - EXPECT_TRUE(capabilities()->GetCapability(
|
| - "dummy_dictionary_key.dummy_field_bool", &value));
|
| + scoped_ptr<base::Value> value =
|
| + capabilities()->GetCapability("dummy_dictionary_key.dummy_field_bool");
|
| + ASSERT_NE(value, nullptr);
|
| EXPECT_TRUE(value->GetAsBoolean(&value_bool));
|
| EXPECT_FALSE(value_bool);
|
|
|
| capabilities()->SetCapability(
|
| "dummy_dictionary_key.dummy_field_int",
|
| make_scoped_ptr(new base::FundamentalValue(100)));
|
| - value = nullptr;
|
| + base::RunLoop().RunUntilIdle();
|
| int value_int = 0;
|
| - EXPECT_TRUE(capabilities()->GetCapability(
|
| - "dummy_dictionary_key.dummy_field_int", &value));
|
| + value = capabilities()->GetCapability("dummy_dictionary_key.dummy_field_int");
|
| + ASSERT_NE(value, nullptr);
|
| EXPECT_TRUE(value->GetAsInteger(&value_int));
|
| EXPECT_EQ(value_int, 100);
|
| }
|
| @@ -363,20 +472,21 @@ TEST_F(DeviceCapabilitiesImplTest, SetCapabilityDictionaryInvalid) {
|
| capabilities()->SetCapability(
|
| "dummy_dictionary_key.dummy_field_bool",
|
| make_scoped_ptr(new base::FundamentalValue(false)));
|
| - const base::Value* value = nullptr;
|
| + base::RunLoop().RunUntilIdle();
|
| bool value_bool = false;
|
| - EXPECT_TRUE(capabilities()->GetCapability(
|
| - "dummy_dictionary_key.dummy_field_bool", &value));
|
| + scoped_ptr<base::Value> value =
|
| + capabilities()->GetCapability("dummy_dictionary_key.dummy_field_bool");
|
| + ASSERT_NE(value, nullptr);
|
| EXPECT_TRUE(value->GetAsBoolean(&value_bool));
|
| EXPECT_TRUE(value_bool);
|
|
|
| capabilities()->SetCapability(
|
| "dummy_dictionary_key.dummy_field_int",
|
| make_scoped_ptr(new base::FundamentalValue(100)));
|
| - value = nullptr;
|
| + base::RunLoop().RunUntilIdle();
|
| int value_int = 0;
|
| - EXPECT_TRUE(capabilities()->GetCapability(
|
| - "dummy_dictionary_key.dummy_field_int", &value));
|
| + value = capabilities()->GetCapability("dummy_dictionary_key.dummy_field_int");
|
| + ASSERT_NE(value, nullptr);
|
| EXPECT_TRUE(value->GetAsInteger(&value_int));
|
| EXPECT_EQ(value_int, 99);
|
| }
|
| @@ -391,17 +501,19 @@ TEST_F(DeviceCapabilitiesImplTest, MergeDictionary) {
|
| ASSERT_NE(dict_value, nullptr);
|
|
|
| capabilities()->MergeDictionary(*dict_value);
|
| + base::RunLoop().RunUntilIdle();
|
|
|
| // First make sure that capabilities get created if they do not exist
|
| - const base::Value* value = nullptr;
|
| bool value_bool = false;
|
| - EXPECT_TRUE(capabilities()->GetCapability("dummy_field_bool", &value));
|
| + scoped_ptr<base::Value> value =
|
| + capabilities()->GetCapability("dummy_field_bool");
|
| + ASSERT_NE(value, nullptr);
|
| EXPECT_TRUE(value->GetAsBoolean(&value_bool));
|
| EXPECT_TRUE(value_bool);
|
|
|
| - value = nullptr;
|
| int value_int = 0;
|
| - EXPECT_TRUE(capabilities()->GetCapability("dummy_field_int", &value));
|
| + value = capabilities()->GetCapability("dummy_field_int");
|
| + ASSERT_NE(value, nullptr);
|
| EXPECT_TRUE(value->GetAsInteger(&value_int));
|
| EXPECT_EQ(value_int, 99);
|
|
|
| @@ -411,17 +523,50 @@ TEST_F(DeviceCapabilitiesImplTest, MergeDictionary) {
|
| ASSERT_TRUE(dict_value->Remove("dummy_field_bool", nullptr));
|
|
|
| capabilities()->MergeDictionary(*dict_value);
|
| + base::RunLoop().RunUntilIdle();
|
|
|
| - value = nullptr;
|
| value_bool = false;
|
| - EXPECT_TRUE(capabilities()->GetCapability("dummy_field_bool", &value));
|
| + value = capabilities()->GetCapability("dummy_field_bool");
|
| + ASSERT_NE(value, nullptr);
|
| EXPECT_TRUE(value->GetAsBoolean(&value_bool));
|
| EXPECT_TRUE(value_bool);
|
|
|
| - value = nullptr;
|
| - EXPECT_TRUE(capabilities()->GetCapability("dummy_field_int", &value));
|
| + value = capabilities()->GetCapability("dummy_field_int");
|
| + ASSERT_NE(value, nullptr);
|
| EXPECT_TRUE(value->GetAsInteger(&value_int));
|
| EXPECT_EQ(value_int, 100);
|
| }
|
|
|
| +// Tests that it is safe to call DeviceCapabilities methods in
|
| +// an Observer's OnCapabilitiesChanged() implementation safely with correct
|
| +// behavior and without deadlocking.
|
| +TEST_F(DeviceCapabilitiesImplTest, OnCapabilitiesChangedSafe) {
|
| + FakeCapabilitiesObserver observer(capabilities());
|
| +
|
| + // Trigger FakeCapabilitiesObserver::OnCapabilitiesChanged()
|
| + capabilities()->SetCapability(
|
| + "dummy_trigger_key", make_scoped_ptr(new base::FundamentalValue(true)));
|
| + base::RunLoop().RunUntilIdle();
|
| +
|
| + // Check that FakeCapabilitiesObserver::OnCapabilitiesChanged() ran and that
|
| + // behavior was successful
|
| + AssertBasicOperationsSuccessful(capabilities());
|
| +}
|
| +
|
| +// Tests that it is safe to call DeviceCapabilities methods in a Validator's
|
| +// Validate() implementation safely with correct behavior and without
|
| +// deadlocking.
|
| +TEST_F(DeviceCapabilitiesImplTest, ValidateSafe) {
|
| + FakeCapabilityManagerComplex manager(capabilities(), "dummy_validate_key");
|
| +
|
| + // Trigger FakeCapabilityManagerComplex::Validate()
|
| + capabilities()->SetCapability(
|
| + "dummy_validate_key", make_scoped_ptr(new base::FundamentalValue(true)));
|
| + base::RunLoop().RunUntilIdle();
|
| +
|
| + // Check that FakeCapabilityManagerComplex::Validate() ran and that behavior
|
| + // was successful
|
| + AssertBasicOperationsSuccessful(capabilities());
|
| +}
|
| +
|
| } // namespace chromecast
|
|
|