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',