shill: PropertyChangeNotifier: Initial commit Add a PropertyObserver, whose task is to track changes to the returned value of an accessor. Further, create a ServicePropertyChangeNotifier, whose task is to monitor a collection of PropertyObservers for a Service and call the appropriate Emit*Property method for those properties which have changed. BUG=chromium:379948 TEST=Unit tests Change-Id: I450b74b388f8ecb44010f277cc77149412b24950 Reviewed-on: https://chromium-review.googlesource.com/202441 Reviewed-by: mukesh agrawal <quiche@chromium.org> Commit-Queue: Paul Stewart <pstew@chromium.org> Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/property_observer.h b/property_observer.h new file mode 100644 index 0000000..e024cdc --- /dev/null +++ b/property_observer.h
@@ -0,0 +1,61 @@ +// Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SHILL_PROPERTY_OBSERVER_ +#define SHILL_PROPERTY_OBSERVER_ + +#include <base/basictypes.h> +#include <base/callback.h> +#include <tr1/memory> + +#include "shill/accessor_interface.h" +#include "shill/error.h" +#include "shill/property_observer_interface.h" + +namespace shill { + +// A templated object that retains a reference to a typed accessor, +// and a saved value retrieved from the accessor. When the update +// method is called, it compares its saved value to the current +// value returned by the accessor. If the value has changed, it +// calls the supplied callback and updates the saved value. +template <class T> +class PropertyObserver : public PropertyObserverInterface { + public: + typedef base::Callback<void(const T &new_value)> Callback; + + PropertyObserver(std::tr1::shared_ptr<AccessorInterface<T>> accessor, + Callback callback) + : accessor_(accessor), callback_(callback) { + Error unused_error; + saved_value_ = accessor_->Get(&unused_error); + } + virtual ~PropertyObserver() {} + + // Implements PropertyObserverInterface. Compares the saved value with + // what the Get() method of |accessor_| returns. If the value has changed + // |callback_| is invoked and |saved_value_| is updated. + virtual void Update() override { + Error error; + T new_value_ = accessor_->Get(&error); + if (!error.IsSuccess() || saved_value_ == new_value_) { + return; + } + callback_.Run(new_value_); + saved_value_ = new_value_; + } + + private: + friend class PropertyObserverTest; + + std::tr1::shared_ptr<AccessorInterface<T>> accessor_; + Callback callback_; + T saved_value_; + + DISALLOW_COPY_AND_ASSIGN(PropertyObserver); +}; + +} // namespace shill + +#endif // SHILL_PROPERTY_OBSERVER_
diff --git a/property_observer_interface.h b/property_observer_interface.h new file mode 100644 index 0000000..608bd8a --- /dev/null +++ b/property_observer_interface.h
@@ -0,0 +1,28 @@ +// Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SHILL_PROPERTY_OBSERVER_INTERFACE_ +#define SHILL_PROPERTY_OBSERVER_INTERFACE_ + +#include <base/basictypes.h> + +namespace shill { + +// An abstract interface for objects that retain a saved copy of +// a property accessor and can report whether that value has changed. +class PropertyObserverInterface { + public: + PropertyObserverInterface() {} + virtual ~PropertyObserverInterface() {} + + // Update the saved value used for comparison. + virtual void Update() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(PropertyObserverInterface); +}; + +} // namespace shill + +#endif // SHILL_PROPERTY_OBSERVER_INTERFACE_
diff --git a/property_observer_unittest.cc b/property_observer_unittest.cc new file mode 100644 index 0000000..3b8e09e --- /dev/null +++ b/property_observer_unittest.cc
@@ -0,0 +1,90 @@ +// Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "shill/property_observer.h" + +#include <string> + +#include <base/bind.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "shill/accessor_interface.h" +#include "shill/error.h" + +using base::Bind; +using base::Unretained; +using testing::_; +using testing::DoAll; +using testing::Mock; +using testing::Return; + +namespace shill { + +class TestPropertyAccessor : public AccessorInterface<bool> { + public: + MOCK_METHOD1(Clear, void(Error *error)); + MOCK_METHOD1(Get, bool(Error *error)); + MOCK_METHOD2(Set, bool(const bool &value, Error *error)); +}; + +class PropertyObserverTest : public testing::Test { + public: + PropertyObserverTest() + : test_accessor_(new TestPropertyAccessor()), + bool_accessor_(test_accessor_) {} + virtual ~PropertyObserverTest() {} + + MOCK_METHOD1(TestCallback, void(const bool &value)); + + // Invoked method during expectations. + void SetError(Error *error) { + error->Populate(Error::kPermissionDenied); + } + + protected: + bool GetSavedValue(const PropertyObserver<bool> &observer) { + return observer.saved_value_; + } + + TestPropertyAccessor *test_accessor_; + BoolAccessor bool_accessor_; // Owns reference to |test_accessor_|. +}; + +TEST_F(PropertyObserverTest, Callback) { + EXPECT_CALL(*test_accessor_, Get(_)).WillOnce(Return(true)); + EXPECT_CALL(*this, TestCallback(_)).Times(0); + PropertyObserver<bool> observer(bool_accessor_, + Bind(&PropertyObserverTest::TestCallback, + Unretained(this))); + EXPECT_TRUE(GetSavedValue(observer)); + Mock::VerifyAndClearExpectations(test_accessor_); + + // Accessor returns an error. + EXPECT_CALL(*test_accessor_, Get(_)) + .WillOnce(DoAll(Invoke(this, &PropertyObserverTest::SetError), + Return(false))); + observer.Update(); + + // Value remains unchanged. + EXPECT_CALL(*test_accessor_, Get(_)).WillOnce(Return(true)); + observer.Update(); + Mock::VerifyAndClearExpectations(test_accessor_); + Mock::VerifyAndClearExpectations(this); + + // Value changes. + EXPECT_CALL(*test_accessor_, Get(_)).WillOnce(Return(false)); + EXPECT_CALL(*this, TestCallback(false)); + observer.Update(); + EXPECT_FALSE(GetSavedValue(observer)); + Mock::VerifyAndClearExpectations(test_accessor_); + Mock::VerifyAndClearExpectations(this); + + // Value remains unchanged (false). + EXPECT_CALL(*test_accessor_, Get(_)).WillOnce(Return(false)); + EXPECT_CALL(*this, TestCallback(_)).Times(0); + observer.Update(); +} + +} // namespace shill
diff --git a/service_property_change_notifier.cc b/service_property_change_notifier.cc new file mode 100644 index 0000000..80d8e3e --- /dev/null +++ b/service_property_change_notifier.cc
@@ -0,0 +1,145 @@ +// Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "shill/service_property_change_notifier.h" + +#include <string> + +#include <base/bind.h> + +#include "shill/adaptor_interfaces.h" +#include "shill/property_observer.h" + +using base::Bind; +using std::string; + +namespace shill { + +ServicePropertyChangeNotifier::ServicePropertyChangeNotifier( + ServiceAdaptorInterface *adaptor) : rpc_adaptor_(adaptor) {} + +ServicePropertyChangeNotifier::~ServicePropertyChangeNotifier() {} + +void ServicePropertyChangeNotifier::AddBoolPropertyObserver( + const string &name, BoolAccessor accessor) { + property_observers_.emplace_back( + new PropertyObserver<bool>( + accessor, + Bind(&ServicePropertyChangeNotifier::BoolPropertyUpdater, + base::Unretained(this), + name))); +} + +void ServicePropertyChangeNotifier::AddUint8PropertyObserver( + const string &name, Uint8Accessor accessor) { + property_observers_.emplace_back( + new PropertyObserver<uint8>( + accessor, + Bind(&ServicePropertyChangeNotifier::Uint8PropertyUpdater, + base::Unretained(this), + name))); +} + +void ServicePropertyChangeNotifier::AddUint16PropertyObserver( + const string &name, Uint16Accessor accessor) { + property_observers_.emplace_back( + new PropertyObserver<uint16>( + accessor, + Bind(&ServicePropertyChangeNotifier::Uint16PropertyUpdater, + base::Unretained(this), + name))); +} + +void ServicePropertyChangeNotifier::AddUint16sPropertyObserver( + const string &name, Uint16sAccessor accessor) { + property_observers_.emplace_back( + new PropertyObserver<Uint16s>( + accessor, + Bind(&ServiceAdaptorInterface::EmitUint16sChanged, + base::Unretained(rpc_adaptor_), + name))); +} + +void ServicePropertyChangeNotifier::AddUintPropertyObserver( + const string &name, Uint32Accessor accessor) { + property_observers_.emplace_back( + new PropertyObserver<uint32>( + accessor, + Bind(&ServicePropertyChangeNotifier::Uint32PropertyUpdater, + base::Unretained(this), + name))); +} + +void ServicePropertyChangeNotifier::AddIntPropertyObserver( + const string &name, Int32Accessor accessor) { + property_observers_.emplace_back( + new PropertyObserver<int32>( + accessor, + Bind(&ServicePropertyChangeNotifier::Int32PropertyUpdater, + base::Unretained(this), + name))); +} + +void ServicePropertyChangeNotifier::AddRpcIdentifierPropertyObserver( + const string &name, RpcIdentifierAccessor accessor) { + property_observers_.emplace_back( + new PropertyObserver<string>( + accessor, + Bind(&ServiceAdaptorInterface::EmitRpcIdentifierChanged, + base::Unretained(rpc_adaptor_), + name))); +} + +void ServicePropertyChangeNotifier::AddStringPropertyObserver( + const string &name, StringAccessor accessor) { + property_observers_.emplace_back( + new PropertyObserver<string>( + accessor, + Bind(&ServiceAdaptorInterface::EmitStringChanged, + base::Unretained(rpc_adaptor_), + name))); +} + +void ServicePropertyChangeNotifier::AddStringmapPropertyObserver( + const string &name, StringmapAccessor accessor) { + property_observers_.emplace_back( + new PropertyObserver<Stringmap>( + accessor, + Bind(&ServiceAdaptorInterface::EmitStringmapChanged, + base::Unretained(rpc_adaptor_), + name))); +} + +void ServicePropertyChangeNotifier::UpdatePropertyObservers() { + for (const auto &observer : property_observers_) { + observer->Update(); + } +} + +void ServicePropertyChangeNotifier::BoolPropertyUpdater(const string &name, + const bool &value) { + rpc_adaptor_->EmitBoolChanged(name, value); +} + +void ServicePropertyChangeNotifier::Uint8PropertyUpdater(const string &name, + const uint8 &value) { + rpc_adaptor_->EmitUint8Changed(name, value); +} + +void ServicePropertyChangeNotifier::Uint16PropertyUpdater(const string &name, + const uint16 &value) { + rpc_adaptor_->EmitUint16Changed(name, value); +} + +void ServicePropertyChangeNotifier::Uint32PropertyUpdater(const string &name, + const uint32 &value) { + rpc_adaptor_->EmitUintChanged(name, value); +} + +void ServicePropertyChangeNotifier::Int32PropertyUpdater(const string &name, + const int32 &value) { + rpc_adaptor_->EmitIntChanged(name, value); +} + +} // namespace shill
diff --git a/service_property_change_notifier.h b/service_property_change_notifier.h new file mode 100644 index 0000000..ef3ffb3 --- /dev/null +++ b/service_property_change_notifier.h
@@ -0,0 +1,68 @@ +// Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SHILL_PROPERTY_CHANGE_NOTIFIER_ +#define SHILL_PROPERTY_CHANGE_NOTIFIER_ + +#include <string> +#include <vector> + +#include <base/basictypes.h> +#include <base/callback.h> +#include <base/memory/scoped_ptr.h> + +#include "shill/accessor_interface.h" + +namespace shill { + +class PropertyObserverInterface; +class ServiceAdaptorInterface; + +// A collection of property observers used by objects to deliver +// property change notifications. This object holds an un-owned +// pointer to the ServiceAdaptor to which notifications should be +// posted. This pointer must be valid for the lifetime of this +// property change notifier. +class ServicePropertyChangeNotifier { + public: + ServicePropertyChangeNotifier(ServiceAdaptorInterface *adaptor); + virtual ~ServicePropertyChangeNotifier(); + + virtual void AddBoolPropertyObserver(const std::string &name, + BoolAccessor accessor); + virtual void AddUint8PropertyObserver(const std::string &name, + Uint8Accessor accessor); + virtual void AddUint16PropertyObserver(const std::string &name, + Uint16Accessor accessor); + virtual void AddUint16sPropertyObserver(const std::string &name, + Uint16sAccessor accessor); + virtual void AddUintPropertyObserver(const std::string &name, + Uint32Accessor accessor); + virtual void AddIntPropertyObserver(const std::string &name, + Int32Accessor accessor); + virtual void AddRpcIdentifierPropertyObserver(const std::string &name, + RpcIdentifierAccessor accessor); + virtual void AddStringPropertyObserver(const std::string &name, + StringAccessor accessor); + virtual void AddStringmapPropertyObserver(const std::string &name, + StringmapAccessor accessor); + virtual void UpdatePropertyObservers(); + + private: + // Redirects templated calls to a value reference to a by-copy version. + void BoolPropertyUpdater(const std::string &name, const bool &value); + void Uint8PropertyUpdater(const std::string &name, const uint8 &value); + void Uint16PropertyUpdater(const std::string &name, const uint16 &value); + void Uint32PropertyUpdater(const std::string &name, const uint32 &value); + void Int32PropertyUpdater(const std::string &name, const int32 &value); + + ServiceAdaptorInterface *rpc_adaptor_; + std::vector<scoped_ptr<PropertyObserverInterface>> property_observers_; + + DISALLOW_COPY_AND_ASSIGN(ServicePropertyChangeNotifier); +}; + +} // namespace shill + +#endif // SHILL_PROPERTY_CHANGE_NOTIFIER_
diff --git a/shill.gyp b/shill.gyp index 22f1ee2..057e80c 100644 --- a/shill.gyp +++ b/shill.gyp
@@ -377,6 +377,7 @@ 'scoped_umask.cc', 'service.cc', 'service_dbus_adaptor.cc', + 'service_property_change_notifier.cc', 'shill_ares.cc', 'shill_config.cc', 'shill_daemon.cc', @@ -659,6 +660,7 @@ 'profile_dbus_property_exporter_unittest.cc', 'profile_unittest.cc', 'property_accessor_unittest.cc', + 'property_observer_unittest.cc', 'property_store_unittest.cc', 'resolver_unittest.cc', 'result_aggregator_unittest.cc',