PM: Generic variable for copying the return value of a function call.

This is useful for quickly implementing variables that rely on calls to
utility functions/methods.

BUG=None
TEST=Unit tests.

Change-Id: I46040070c417b8c1f7b6081d4fe4d3b43a7a8101
Reviewed-on: https://chromium-review.googlesource.com/200659
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
Commit-Queue: Gilad Arnold <garnold@chromium.org>
diff --git a/policy_manager/generic_variables.h b/policy_manager/generic_variables.h
index 5309519..42e991d 100644
--- a/policy_manager/generic_variables.h
+++ b/policy_manager/generic_variables.h
@@ -9,6 +9,8 @@
 #ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_POLICY_MANAGER_GENERIC_VARIABLES_H_
 #define CHROMEOS_PLATFORM_UPDATE_ENGINE_POLICY_MANAGER_GENERIC_VARIABLES_H_
 
+#include <base/callback.h>
+
 #include "update_engine/policy_manager/variable.h"
 
 namespace {
@@ -118,6 +120,37 @@
   const T obj_;
 };
 
+// Variable class returning a copy of a value returned by a given function. The
+// function is called every time the variable is being polled.
+template<typename T>
+class CallCopyVariable : public Variable<T> {
+ public:
+  CallCopyVariable(const std::string& name, base::Callback<T(void)> func)
+      : Variable<T>(name, kVariableModePoll), func_(func) {}
+  CallCopyVariable(const std::string& name,
+                   const base::TimeDelta poll_interval,
+                   base::Callback<T(void)> func)
+      : Variable<T>(name, poll_interval), func_(func) {}
+
+ protected:
+  // Variable override.
+  virtual const T* GetValue(base::TimeDelta /* timeout */,
+                            std::string* /* errmsg */) {
+    if (func_.is_null())
+      return nullptr;
+    return new T(func_.Run());
+  }
+
+ private:
+  FRIEND_TEST(PmCallCopyVariableTest, SimpleTest);
+
+  // The function to be called, stored as a base::Callback.
+  base::Callback<T(void)> func_;
+
+  DISALLOW_COPY_AND_ASSIGN(CallCopyVariable);
+};
+
+
 // A Variable class to implement simple Async variables. It provides two methods
 // SetValue and UnsetValue to modify the current value of the variable and
 // notify the registered observers whenever the value changed.
diff --git a/policy_manager/generic_variables_unittest.cc b/policy_manager/generic_variables_unittest.cc
index 8aad753..279c729 100644
--- a/policy_manager/generic_variables_unittest.cc
+++ b/policy_manager/generic_variables_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "update_engine/policy_manager/generic_variables.h"
 
+#include <base/callback.h>
 #include <base/memory/scoped_ptr.h>
 #include <gtest/gtest.h>
 
@@ -57,11 +58,14 @@
 class CopyConstructorTestClass {
  public:
   CopyConstructorTestClass(void) : copied_(false) {}
-  CopyConstructorTestClass(const CopyConstructorTestClass& /* ignored */)
-      : copied_(true) {}
+  CopyConstructorTestClass(const CopyConstructorTestClass& other)
+      : copied_(true), val_(other.val_ * 2) {}
 
   // Tells if the instance was constructed using the copy-constructor.
-  bool copied_;
+  const bool copied_;
+
+  // An auxiliary internal value.
+  int val_ = 0;
 };
 
 
@@ -91,6 +95,41 @@
 }
 
 
+class PmCallCopyVariableTest : public ::testing::Test {};
+
+CopyConstructorTestClass test_func(CopyConstructorTestClass* obj) {
+  obj->val_++;  // So we can check that the function was called.
+  return *obj;
+}
+
+TEST_F(PmCallCopyVariableTest, SimpleTest) {
+  // Tests that the returned value is generated by copying the value returned by
+  // the function call.
+
+  CopyConstructorTestClass test_obj;
+  ASSERT_FALSE(test_obj.copied_);
+  test_obj.val_ = 5;
+
+  base::Callback<CopyConstructorTestClass(void)> cb = base::Bind(
+      test_func, &test_obj);
+  CallCopyVariable<CopyConstructorTestClass> var("var", cb);
+
+  scoped_ptr<const CopyConstructorTestClass> copy(
+      var.GetValue(PmTestUtils::DefaultTimeout(), nullptr));
+  EXPECT_EQ(6, test_obj.val_);  // Check that the function was called.
+  PMTEST_ASSERT_NOT_NULL(copy.get());
+  EXPECT_TRUE(copy->copied_);
+  EXPECT_EQ(12, copy->val_);  // Check that copying occurred once.
+}
+
+TEST_F(PmCallCopyVariableTest, NullTest) {
+  // Ensures that the variable returns null when the callback is null.
+
+  base::Callback<bool(void)> cb;
+  CallCopyVariable<bool> var("var", cb);
+  PmTestUtils::ExpectVariableNotSet(&var);
+}
+
 class PmAsyncCopyVariableTest : public ::testing::Test {
  public:
   void TearDown() {