PolicyManager: Schedule re-evaluations based on variable usage.
This patch makes the EvaluationContext re-schedule a policy request
based on the variables used by that method, waiting for the Async
variables and polling the Poll variables on the suggested interval.
In order to use the main loop functions from the EvaluationContext
they were moved to its own file called event_loop.h.
BUG=chromium:340871
TEST=Unit tests added.
Change-Id: Ibfc52e4dfd12c5e1ef87b5ad9cc318f9821dcfdd
Reviewed-on: https://chromium-review.googlesource.com/190424
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Commit-Queue: Alex Deymo <deymo@chromium.org>
Tested-by: Alex Deymo <deymo@chromium.org>
diff --git a/policy_manager/evaluation_context_unittest.cc b/policy_manager/evaluation_context_unittest.cc
index 6ef6f74..d56b5a2 100644
--- a/policy_manager/evaluation_context_unittest.cc
+++ b/policy_manager/evaluation_context_unittest.cc
@@ -2,30 +2,69 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <base/memory/scoped_ptr.h>
-#include <gtest/gtest.h>
#include <string>
+#include <base/bind.h>
+#include <base/memory/scoped_ptr.h>
+#include <gtest/gtest.h>
+
#include "update_engine/policy_manager/evaluation_context.h"
+#include "update_engine/policy_manager/fake_variable.h"
#include "update_engine/policy_manager/generic_variables.h"
#include "update_engine/policy_manager/pmtest_utils.h"
-#include "update_engine/policy_manager/fake_variable.h"
+#include "update_engine/test_utils.h"
+using base::Bind;
+using base::TimeDelta;
+using chromeos_update_engine::RunGMainLoopMaxIterations;
+using chromeos_update_engine::RunGMainLoopUntil;
using std::string;
+namespace {
+
+void DoNothing() {}
+
+// Sets the value of the passed pointer to true.
+void SetTrue(bool* value) {
+ *value = true;
+}
+
+bool GetBoolean(bool* value) {
+ return *value;
+}
+
+} // namespace
+
namespace chromeos_policy_manager {
class PmEvaluationContextTest : public ::testing::Test {
public:
- PmEvaluationContextTest() : fake_int_var_("fake_int", kVariableModePoll) {}
+ PmEvaluationContextTest() {}
protected:
virtual void SetUp() {
eval_ctx_ = new EvaluationContext();
}
+ virtual void TearDown() {
+ eval_ctx_ = NULL;
+ // Check that the evaluation context removed all the observers.
+ EXPECT_TRUE(fake_int_var_.observer_list_.empty());
+ EXPECT_TRUE(fake_async_var_.observer_list_.empty());
+ EXPECT_TRUE(fake_const_var_.observer_list_.empty());
+ EXPECT_TRUE(fake_poll_var_.observer_list_.empty());
+ }
+
scoped_refptr<EvaluationContext> eval_ctx_;
- FakeVariable<int> fake_int_var_;
+
+ // FakeVariables used for testing the EvaluationContext. These are required
+ // here to prevent them from going away *before* the EvaluationContext under
+ // test does, which keeps a reference to them.
+ FakeVariable<int> fake_int_var_ = {"fake_int", kVariableModePoll};
+ FakeVariable<string> fake_async_var_ = {"fake_async", kVariableModeAsync};
+ FakeVariable<string> fake_const_var_ = {"fake_const", kVariableModeConst};
+ FakeVariable<string> fake_poll_var_ = {"fake_poll",
+ TimeDelta::FromSeconds(1)};
};
TEST_F(PmEvaluationContextTest, GetValueFails) {
@@ -75,16 +114,15 @@
}
TEST_F(PmEvaluationContextTest, GetValueMixedTypes) {
- FakeVariable<string> fake_string_var_("fake_string", kVariableModePoll);
const int* p_fake_int;
const string* p_fake_string;
fake_int_var_.reset(new int(42));
- fake_string_var_.reset(new string("Hello world!"));
+ fake_poll_var_.reset(new string("Hello world!"));
// Check that the EvaluationContext can handle multiple Variable types. This
// is mostly a compile-time check due to the template nature of this method.
p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
- p_fake_string = eval_ctx_->GetValue(&fake_string_var_);
+ p_fake_string = eval_ctx_->GetValue(&fake_poll_var_);
PMTEST_ASSERT_NOT_NULL(p_fake_int);
EXPECT_EQ(42, *p_fake_int);
@@ -93,4 +131,76 @@
EXPECT_EQ("Hello world!", *p_fake_string);
}
+// Test that we don't schedule an event if there's no variable to wait for.
+TEST_F(PmEvaluationContextTest, RunOnValueChangeOrTimeoutWithoutVariablesTest) {
+ fake_const_var_.reset(new string("Hello world!"));
+ EXPECT_EQ(*eval_ctx_->GetValue(&fake_const_var_), "Hello world!");
+
+ EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
+}
+
+// Test that we don't schedule an event if there's no variable to wait for.
+TEST_F(PmEvaluationContextTest, RunOnValueChangeOrTimeoutWithVariablesTest) {
+ fake_async_var_.reset(new string("Async value"));
+ eval_ctx_->GetValue(&fake_async_var_);
+
+ bool value = false;
+ EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+ // Check that the scheduled callback isn't run until we signal a ValueChaged.
+ RunGMainLoopMaxIterations(100);
+ EXPECT_FALSE(value);
+
+ fake_async_var_.NotifyValueChanged();
+ EXPECT_FALSE(value);
+ // Ensure that the scheduled callback isn't run until we are back on the main
+ // loop.
+ RunGMainLoopMaxIterations(100);
+ EXPECT_TRUE(value);
+}
+
+// Test that we don't re-schedule the events if we are attending one.
+TEST_F(PmEvaluationContextTest, RunOnValueChangeOrTimeoutCalledTwiceTest) {
+ fake_async_var_.reset(new string("Async value"));
+ eval_ctx_->GetValue(&fake_async_var_);
+
+ bool value = false;
+ EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+ EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+
+ // The scheduled event should still work.
+ fake_async_var_.NotifyValueChanged();
+ RunGMainLoopMaxIterations(100);
+ EXPECT_TRUE(value);
+}
+
+// Test that we clear the events when destroying the EvaluationContext.
+TEST_F(PmEvaluationContextTest, RemoveObserversAndTimeoutTest) {
+ fake_async_var_.reset(new string("Async value"));
+ eval_ctx_->GetValue(&fake_async_var_);
+
+ bool value = false;
+ EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+ eval_ctx_ = NULL;
+
+ // This should not trigger the callback since the EvaluationContext waiting
+ // for it is gone, and it should have remove all its observers.
+ fake_async_var_.NotifyValueChanged();
+ RunGMainLoopMaxIterations(100);
+ EXPECT_FALSE(value);
+}
+
+// Test that we don't schedule an event if there's no variable to wait for.
+TEST_F(PmEvaluationContextTest, RunOnValueChangeOrTimeoutRunsFromTimeoutTest) {
+ fake_poll_var_.reset(new string("Polled value"));
+ eval_ctx_->GetValue(&fake_poll_var_);
+
+ bool value = false;
+ EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
+ // Check that the scheduled callback isn't run until the timeout occurs.
+ RunGMainLoopMaxIterations(10);
+ EXPECT_FALSE(value);
+ RunGMainLoopUntil(10000, Bind(&GetBoolean, &value));
+ EXPECT_TRUE(value);
+}
+
} // namespace chromeos_policy_manager