PolicyManager: Add an Observer interface to notify value changes.

This patch uses the Observer design pattern to expose the value
changed event on kVariableModeAsync variables. This will be consumed
by the EvaluationContext whenever it needs to wait for a value change
to re-evaluate the policy.

BUG=chromium:341209
TEST=Unit tests added and passing.

Change-Id: I7b3939751d49270650252c0fb0dbcc1f6ec92930
Reviewed-on: https://chromium-review.googlesource.com/187900
Reviewed-by: Alex Deymo <deymo@chromium.org>
Tested-by: Alex Deymo <deymo@chromium.org>
Commit-Queue: Alex Deymo <deymo@chromium.org>
diff --git a/policy_manager/variable.h b/policy_manager/variable.h
index 373af7f..28a9fb2 100644
--- a/policy_manager/variable.h
+++ b/policy_manager/variable.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_POLICY_MANAGER_VARIABLE_H
 #define CHROMEOS_PLATFORM_UPDATE_ENGINE_POLICY_MANAGER_VARIABLE_H
 
+#include <algorithm>
+#include <list>
 #include <string>
 
 #include <base/time.h>
@@ -37,6 +39,15 @@
 // deppend on the variable's type, implemented by all the variables.
 class BaseVariable {
  public:
+  // Interface for observing changes on variable value.
+  class Observer {
+   public:
+    virtual ~Observer() {}
+
+    // Called when the value on the variable changes.
+    virtual void ValueChanged(BaseVariable* variable) = 0;
+  };
+
   virtual ~BaseVariable() {}
 
   // Returns the variable name as a string.
@@ -55,6 +66,20 @@
     return poll_interval_;
   }
 
+  // Adds and removes observers for value changes on the variable. This only
+  // works for kVariableAsync variables since the other modes don't track value
+  // changes. Adding the same observer twice has no effect.
+  virtual void AddObserver(BaseVariable::Observer* observer) {
+    if (std::find(observer_list_.begin(), observer_list_.end(), observer) ==
+        observer_list_.end()) {
+      observer_list_.push_back(observer);
+    }
+  }
+
+  virtual void RemoveObserver(BaseVariable::Observer* observer) {
+    observer_list_.remove(observer);
+  }
+
  protected:
   // Creates a BaseVariable using the default polling interval (5 minutes).
   BaseVariable(const std::string& name, VariableMode mode)
@@ -66,7 +91,17 @@
   BaseVariable(const std::string& name, base::TimeDelta poll_interval)
       : BaseVariable(name, kVariableModePoll, poll_interval) {}
 
+  // Calls ValueChanged on all the observers.
+  void NotifyValueChanged() {
+    for (auto& observer : observer_list_)
+      observer->ValueChanged(this);
+  }
+
  private:
+  friend class PmBaseVariableTest;
+  FRIEND_TEST(PmBaseVariableTest, RepeatedObserverTest);
+  FRIEND_TEST(PmBaseVariableTest, NotifyValueChangedTest);
+
   BaseVariable(const std::string& name, VariableMode mode,
                base::TimeDelta poll_interval)
     : name_(name), mode_(mode),
@@ -85,6 +120,9 @@
   // The variable's polling interval for VariableModePoll variable and 0 for
   // other modes.
   const base::TimeDelta poll_interval_;
+
+  // The list of value changes observers.
+  std::list<BaseVariable::Observer*> observer_list_;
 };
 
 // Interface to a Policy Manager variable of a given type. Implementation
diff --git a/policy_manager/variable_unittest.cc b/policy_manager/variable_unittest.cc
index 5d3b8d9..2a6824a 100644
--- a/policy_manager/variable_unittest.cc
+++ b/policy_manager/variable_unittest.cc
@@ -2,12 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <vector>
+
 #include <gtest/gtest.h>
 
 #include "update_engine/policy_manager/variable.h"
 
 using base::TimeDelta;
 using std::string;
+using std::vector;
 
 namespace chromeos_policy_manager {
 
@@ -56,4 +59,47 @@
   EXPECT_EQ(var.GetPollInterval(), TimeDelta::FromMinutes(3));
 }
 
+class BaseVariableObserver : public BaseVariable::Observer {
+ public:
+  void ValueChanged(BaseVariable* variable) {
+    calls_.push_back(variable);
+  }
+
+  // List of called functions.
+  vector<BaseVariable*> calls_;
+};
+
+TEST(PmBaseVariableTest, RepeatedObserverTest) {
+  DefaultVariable<int> var("var", kVariableModeAsync);
+  BaseVariableObserver observer;
+  var.AddObserver(&observer);
+  EXPECT_EQ(var.observer_list_.size(), 1);
+  var.AddObserver(&observer);
+  EXPECT_EQ(var.observer_list_.size(), 1);
+  var.RemoveObserver(&observer);
+  EXPECT_EQ(var.observer_list_.size(), 0);
+  var.RemoveObserver(&observer);
+  EXPECT_EQ(var.observer_list_.size(), 0);
+}
+
+TEST(PmBaseVariableTest, NotifyValueChangedTest) {
+  DefaultVariable<int> var("var", kVariableModeAsync);
+  BaseVariableObserver observer1;
+  var.AddObserver(&observer1);
+  // Simulate a value change on the variable's implementation.
+  var.NotifyValueChanged();
+
+  ASSERT_EQ(observer1.calls_.size(), 1);
+  // Check that the observer is called with the right argument.
+  EXPECT_EQ(observer1.calls_[0], &var);
+
+  BaseVariableObserver observer2;
+  var.AddObserver(&observer2);
+  var.NotifyValueChanged();
+
+  // Check that all the observers are called.
+  EXPECT_EQ(observer1.calls_.size(), 2);
+  EXPECT_EQ(observer2.calls_.size(), 1);
+}
+
 }  // namespace chromeos_policy_manager