Home | History | Annotate | Download | only in update_manager
      1 //
      2 // Copyright (C) 2014 The Android Open Source Project
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      http://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 //
     16 
     17 #include "update_engine/update_manager/evaluation_context.h"
     18 
     19 #include <memory>
     20 #include <string>
     21 
     22 #include <base/bind.h>
     23 #include <brillo/message_loops/fake_message_loop.h>
     24 #include <brillo/message_loops/message_loop_utils.h>
     25 #include <gtest/gtest.h>
     26 
     27 #include "update_engine/common/fake_clock.h"
     28 #include "update_engine/update_manager/fake_variable.h"
     29 #include "update_engine/update_manager/generic_variables.h"
     30 #include "update_engine/update_manager/mock_variable.h"
     31 #include "update_engine/update_manager/umtest_utils.h"
     32 
     33 using base::Bind;
     34 using base::Closure;
     35 using base::Time;
     36 using base::TimeDelta;
     37 using brillo::MessageLoop;
     38 using brillo::MessageLoopRunMaxIterations;
     39 using brillo::MessageLoopRunUntil;
     40 using chromeos_update_engine::FakeClock;
     41 using std::string;
     42 using std::unique_ptr;
     43 using testing::Return;
     44 using testing::StrictMock;
     45 using testing::_;
     46 
     47 namespace chromeos_update_manager {
     48 
     49 namespace {
     50 
     51 void DoNothing() {}
     52 
     53 // Sets the value of the passed pointer to true.
     54 void SetTrue(bool* value) {
     55   *value = true;
     56 }
     57 
     58 bool GetBoolean(bool* value) {
     59   return *value;
     60 }
     61 
     62 template<typename T>
     63 void ReadVar(scoped_refptr<EvaluationContext> ec, Variable<T>* var) {
     64   ec->GetValue(var);
     65 }
     66 
     67 // Runs |evaluation|; if the value pointed by |count_p| is greater than zero,
     68 // decrement it and schedule a reevaluation; otherwise, writes true to |done_p|.
     69 void EvaluateRepeatedly(Closure evaluation, scoped_refptr<EvaluationContext> ec,
     70                         int* count_p, bool* done_p) {
     71   evaluation.Run();
     72 
     73   // Schedule reevaluation if needed.
     74   if (*count_p > 0) {
     75     Closure closure = Bind(EvaluateRepeatedly, evaluation, ec, count_p, done_p);
     76     ASSERT_TRUE(ec->RunOnValueChangeOrTimeout(closure))
     77         << "Failed to schedule reevaluation, count_p=" << *count_p;
     78     (*count_p)--;
     79   } else {
     80     *done_p = true;
     81   }
     82 }
     83 
     84 }  // namespace
     85 
     86 class UmEvaluationContextTest : public ::testing::Test {
     87  protected:
     88   void SetUp() override {
     89     loop_.SetAsCurrent();
     90     // Apr 22, 2009 19:25:00 UTC (this is a random reference point).
     91     fake_clock_.SetMonotonicTime(Time::FromTimeT(1240428300));
     92     // Mar 2, 2006 1:23:45 UTC.
     93     fake_clock_.SetWallclockTime(Time::FromTimeT(1141262625));
     94     eval_ctx_ = new EvaluationContext(
     95         &fake_clock_, default_timeout_, default_timeout_,
     96         unique_ptr<base::Callback<void(EvaluationContext*)>>(nullptr));
     97   }
     98 
     99   void TearDown() override {
    100     // Ensure that the evaluation context did not leak and is actually being
    101     // destroyed.
    102     if (eval_ctx_) {
    103       base::WeakPtr<EvaluationContext> eval_ctx_weak_alias =
    104           eval_ctx_->weak_ptr_factory_.GetWeakPtr();
    105       ASSERT_NE(nullptr, eval_ctx_weak_alias.get());
    106       eval_ctx_ = nullptr;
    107       EXPECT_EQ(nullptr, eval_ctx_weak_alias.get())
    108           << "The evaluation context was not destroyed! This is likely a bug "
    109              "in how the test was written, look for leaking handles to the EC, "
    110              "possibly through closure objects.";
    111     }
    112 
    113     // Check that the evaluation context removed all the observers.
    114     EXPECT_TRUE(fake_int_var_.observer_list_.empty());
    115     EXPECT_TRUE(fake_async_var_.observer_list_.empty());
    116     EXPECT_TRUE(fake_const_var_.observer_list_.empty());
    117     EXPECT_TRUE(fake_poll_var_.observer_list_.empty());
    118 
    119     EXPECT_FALSE(loop_.PendingTasks());
    120   }
    121 
    122   TimeDelta default_timeout_ = TimeDelta::FromSeconds(5);
    123 
    124   brillo::FakeMessageLoop loop_{nullptr};
    125   FakeClock fake_clock_;
    126   scoped_refptr<EvaluationContext> eval_ctx_;
    127 
    128   // FakeVariables used for testing the EvaluationContext. These are required
    129   // here to prevent them from going away *before* the EvaluationContext under
    130   // test does, which keeps a reference to them.
    131   FakeVariable<bool> fail_var_ = {"fail_var", kVariableModePoll};
    132   FakeVariable<int> fake_int_var_ = {"fake_int", kVariableModePoll};
    133   FakeVariable<string> fake_async_var_ = {"fake_async", kVariableModeAsync};
    134   FakeVariable<string> fake_const_var_ = {"fake_const", kVariableModeConst};
    135   FakeVariable<string> fake_poll_var_ = {"fake_poll",
    136                                          TimeDelta::FromSeconds(1)};
    137   StrictMock<MockVariable<string>> mock_var_async_ {
    138     "mock_var_async", kVariableModeAsync};
    139   StrictMock<MockVariable<string>> mock_var_poll_ {
    140     "mock_var_poll", kVariableModePoll};
    141 };
    142 
    143 TEST_F(UmEvaluationContextTest, GetValueFails) {
    144   // FakeVariable is initialized as returning null.
    145   EXPECT_EQ(nullptr, eval_ctx_->GetValue(&fake_int_var_));
    146 }
    147 
    148 TEST_F(UmEvaluationContextTest, GetValueFailsWithInvalidVar) {
    149   EXPECT_EQ(nullptr, eval_ctx_->GetValue(static_cast<Variable<int>*>(nullptr)));
    150 }
    151 
    152 TEST_F(UmEvaluationContextTest, GetValueReturns) {
    153   const int* p_fake_int;
    154 
    155   fake_int_var_.reset(new int(42));
    156   p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
    157   ASSERT_NE(nullptr, p_fake_int);
    158   EXPECT_EQ(42, *p_fake_int);
    159 }
    160 
    161 TEST_F(UmEvaluationContextTest, GetValueCached) {
    162   const int* p_fake_int;
    163 
    164   fake_int_var_.reset(new int(42));
    165   p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
    166 
    167   // Check that if the variable changes, the EvaluationContext keeps returning
    168   // the cached value.
    169   fake_int_var_.reset(new int(5));
    170 
    171   p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
    172   ASSERT_NE(nullptr, p_fake_int);
    173   EXPECT_EQ(42, *p_fake_int);
    174 }
    175 
    176 TEST_F(UmEvaluationContextTest, GetValueCachesNull) {
    177   const int* p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
    178   EXPECT_EQ(nullptr, p_fake_int);
    179 
    180   fake_int_var_.reset(new int(42));
    181   // A second attempt to read the variable should not work because this
    182   // EvaluationContext already got a null value.
    183   p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
    184   EXPECT_EQ(nullptr, p_fake_int);
    185 }
    186 
    187 TEST_F(UmEvaluationContextTest, GetValueMixedTypes) {
    188   const int* p_fake_int;
    189   const string* p_fake_string;
    190 
    191   fake_int_var_.reset(new int(42));
    192   fake_poll_var_.reset(new string("Hello world!"));
    193   // Check that the EvaluationContext can handle multiple Variable types. This
    194   // is mostly a compile-time check due to the template nature of this method.
    195   p_fake_int = eval_ctx_->GetValue(&fake_int_var_);
    196   p_fake_string = eval_ctx_->GetValue(&fake_poll_var_);
    197 
    198   ASSERT_NE(nullptr, p_fake_int);
    199   EXPECT_EQ(42, *p_fake_int);
    200 
    201   ASSERT_NE(nullptr, p_fake_string);
    202   EXPECT_EQ("Hello world!", *p_fake_string);
    203 }
    204 
    205 // Test that we don't schedule an event if there's no variable to wait for.
    206 TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutWithoutVariables) {
    207   fake_const_var_.reset(new string("Hello world!"));
    208   EXPECT_EQ(*eval_ctx_->GetValue(&fake_const_var_), "Hello world!");
    209 
    210   EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
    211 }
    212 
    213 // Test that reevaluation occurs when an async variable it depends on changes.
    214 TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutWithVariables) {
    215   fake_async_var_.reset(new string("Async value"));
    216   eval_ctx_->GetValue(&fake_async_var_);
    217 
    218   bool value = false;
    219   EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
    220   // Check that the scheduled callback isn't run until we signal a ValueChaged.
    221   MessageLoopRunMaxIterations(MessageLoop::current(), 100);
    222   EXPECT_FALSE(value);
    223 
    224   fake_async_var_.NotifyValueChanged();
    225   EXPECT_FALSE(value);
    226   // Ensure that the scheduled callback isn't run until we are back on the main
    227   // loop.
    228   MessageLoopRunMaxIterations(MessageLoop::current(), 100);
    229   EXPECT_TRUE(value);
    230 }
    231 
    232 // Test that we don't re-schedule the events if we are attending one.
    233 TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutCalledTwice) {
    234   fake_async_var_.reset(new string("Async value"));
    235   eval_ctx_->GetValue(&fake_async_var_);
    236 
    237   bool value = false;
    238   EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
    239   EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
    240 
    241   // The scheduled event should still work.
    242   fake_async_var_.NotifyValueChanged();
    243   MessageLoopRunMaxIterations(MessageLoop::current(), 100);
    244   EXPECT_TRUE(value);
    245 }
    246 
    247 // Test that reevaluation occurs when a polling timeout fires.
    248 TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutRunsFromTimeout) {
    249   fake_poll_var_.reset(new string("Polled value"));
    250   eval_ctx_->GetValue(&fake_poll_var_);
    251 
    252   bool value = false;
    253   EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
    254   // Check that the scheduled callback isn't run until the timeout occurs.
    255   MessageLoopRunMaxIterations(MessageLoop::current(), 10);
    256   EXPECT_FALSE(value);
    257   MessageLoopRunUntil(MessageLoop::current(),
    258                       TimeDelta::FromSeconds(10),
    259                       Bind(&GetBoolean, &value));
    260   EXPECT_TRUE(value);
    261 }
    262 
    263 // Test that callback is called when evaluation context expires, and that it
    264 // cannot be used again unless the expiration deadline is reset.
    265 TEST_F(UmEvaluationContextTest, RunOnValueChangeOrTimeoutExpires) {
    266   fake_async_var_.reset(new string("Async value"));
    267   eval_ctx_->GetValue(&fake_async_var_);
    268 
    269   bool value = false;
    270   EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
    271   // Check that the scheduled callback isn't run until the timeout occurs.
    272   MessageLoopRunMaxIterations(MessageLoop::current(), 10);
    273   EXPECT_FALSE(value);
    274   MessageLoopRunUntil(MessageLoop::current(),
    275                       TimeDelta::FromSeconds(10),
    276                       Bind(&GetBoolean, &value));
    277   EXPECT_TRUE(value);
    278 
    279   // Ensure that we cannot reschedule an evaluation.
    280   EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
    281 
    282   // Ensure that we can reschedule an evaluation after resetting expiration.
    283   eval_ctx_->ResetExpiration();
    284   EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
    285 }
    286 
    287 // Test that we clear the events when destroying the EvaluationContext.
    288 TEST_F(UmEvaluationContextTest, RemoveObserversAndTimeoutTest) {
    289   fake_async_var_.reset(new string("Async value"));
    290   eval_ctx_->GetValue(&fake_async_var_);
    291 
    292   bool value = false;
    293   EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
    294   eval_ctx_ = nullptr;
    295 
    296   // This should not trigger the callback since the EvaluationContext waiting
    297   // for it is gone, and it should have remove all its observers.
    298   fake_async_var_.NotifyValueChanged();
    299   MessageLoopRunMaxIterations(MessageLoop::current(), 100);
    300   EXPECT_FALSE(value);
    301 }
    302 
    303 // Scheduling two reevaluations from the callback should succeed.
    304 TEST_F(UmEvaluationContextTest,
    305        RunOnValueChangeOrTimeoutReevaluatesRepeatedly) {
    306   fake_poll_var_.reset(new string("Polled value"));
    307   Closure evaluation = Bind(ReadVar<string>, eval_ctx_, &fake_poll_var_);
    308   int num_reevaluations = 2;
    309   bool done = false;
    310 
    311   // Run the evaluation once.
    312   evaluation.Run();
    313 
    314   // Schedule repeated reevaluations.
    315   Closure closure = Bind(EvaluateRepeatedly, evaluation, eval_ctx_,
    316                          &num_reevaluations, &done);
    317   ASSERT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(closure));
    318   MessageLoopRunUntil(MessageLoop::current(),
    319                       TimeDelta::FromSeconds(10),
    320                       Bind(&GetBoolean, &done));
    321   EXPECT_EQ(0, num_reevaluations);
    322 }
    323 
    324 // Test that we can delete the EvaluationContext while having pending events.
    325 TEST_F(UmEvaluationContextTest, ObjectDeletedWithPendingEventsTest) {
    326   fake_async_var_.reset(new string("Async value"));
    327   fake_poll_var_.reset(new string("Polled value"));
    328   eval_ctx_->GetValue(&fake_async_var_);
    329   eval_ctx_->GetValue(&fake_poll_var_);
    330   EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
    331   // TearDown() checks for leaked observers on this async_variable, which means
    332   // that our object is still alive after removing its reference.
    333 }
    334 
    335 // Test that timed events fired after removal of the EvaluationContext don't
    336 // crash.
    337 TEST_F(UmEvaluationContextTest, TimeoutEventAfterDeleteTest) {
    338   FakeVariable<string> fake_short_poll_var = {"fake_short_poll",
    339                                               TimeDelta::FromSeconds(1)};
    340   fake_short_poll_var.reset(new string("Polled value"));
    341   eval_ctx_->GetValue(&fake_short_poll_var);
    342   bool value = false;
    343   EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&SetTrue, &value)));
    344   // Remove the last reference to the EvaluationContext and run the loop for
    345   // 10 seconds to give time to the main loop to trigger the timeout Event (of 1
    346   // second). Our callback should not be called because the EvaluationContext
    347   // was removed before the timeout event is attended.
    348   eval_ctx_ = nullptr;
    349   MessageLoopRunUntil(MessageLoop::current(),
    350                       TimeDelta::FromSeconds(10),
    351                       Bind(&GetBoolean, &value));
    352   EXPECT_FALSE(value);
    353 }
    354 
    355 TEST_F(UmEvaluationContextTest, DefaultTimeout) {
    356   // Test that the evaluation timeout calculation uses the default timeout on
    357   // setup.
    358   EXPECT_CALL(mock_var_async_, GetValue(default_timeout_, _))
    359       .WillOnce(Return(nullptr));
    360   EXPECT_EQ(nullptr, eval_ctx_->GetValue(&mock_var_async_));
    361 }
    362 
    363 TEST_F(UmEvaluationContextTest, TimeoutUpdatesWithMonotonicTime) {
    364   fake_clock_.SetMonotonicTime(
    365       fake_clock_.GetMonotonicTime() + TimeDelta::FromSeconds(1));
    366 
    367   TimeDelta timeout = default_timeout_ - TimeDelta::FromSeconds(1);
    368 
    369   EXPECT_CALL(mock_var_async_, GetValue(timeout, _))
    370       .WillOnce(Return(nullptr));
    371   EXPECT_EQ(nullptr, eval_ctx_->GetValue(&mock_var_async_));
    372 }
    373 
    374 TEST_F(UmEvaluationContextTest, ResetEvaluationResetsTimesWallclock) {
    375   Time cur_time = fake_clock_.GetWallclockTime();
    376   // Advance the time on the clock but don't call ResetEvaluation yet.
    377   fake_clock_.SetWallclockTime(cur_time + TimeDelta::FromSeconds(4));
    378 
    379   EXPECT_TRUE(eval_ctx_->IsWallclockTimeGreaterThan(
    380           cur_time - TimeDelta::FromSeconds(1)));
    381   EXPECT_FALSE(eval_ctx_->IsWallclockTimeGreaterThan(cur_time));
    382   EXPECT_FALSE(eval_ctx_->IsWallclockTimeGreaterThan(
    383           cur_time + TimeDelta::FromSeconds(1)));
    384   // Call ResetEvaluation now, which should use the new evaluation time.
    385   eval_ctx_->ResetEvaluation();
    386 
    387   cur_time = fake_clock_.GetWallclockTime();
    388   EXPECT_TRUE(eval_ctx_->IsWallclockTimeGreaterThan(
    389           cur_time - TimeDelta::FromSeconds(1)));
    390   EXPECT_FALSE(eval_ctx_->IsWallclockTimeGreaterThan(cur_time));
    391   EXPECT_FALSE(eval_ctx_->IsWallclockTimeGreaterThan(
    392           cur_time + TimeDelta::FromSeconds(1)));
    393 }
    394 
    395 TEST_F(UmEvaluationContextTest, ResetEvaluationResetsTimesMonotonic) {
    396   Time cur_time = fake_clock_.GetMonotonicTime();
    397   // Advance the time on the clock but don't call ResetEvaluation yet.
    398   fake_clock_.SetMonotonicTime(cur_time + TimeDelta::FromSeconds(4));
    399 
    400   EXPECT_TRUE(eval_ctx_->IsMonotonicTimeGreaterThan(
    401           cur_time - TimeDelta::FromSeconds(1)));
    402   EXPECT_FALSE(eval_ctx_->IsMonotonicTimeGreaterThan(cur_time));
    403   EXPECT_FALSE(eval_ctx_->IsMonotonicTimeGreaterThan(
    404           cur_time + TimeDelta::FromSeconds(1)));
    405   // Call ResetEvaluation now, which should use the new evaluation time.
    406   eval_ctx_->ResetEvaluation();
    407 
    408   cur_time = fake_clock_.GetMonotonicTime();
    409   EXPECT_TRUE(eval_ctx_->IsMonotonicTimeGreaterThan(
    410           cur_time - TimeDelta::FromSeconds(1)));
    411   EXPECT_FALSE(eval_ctx_->IsMonotonicTimeGreaterThan(cur_time));
    412   EXPECT_FALSE(eval_ctx_->IsMonotonicTimeGreaterThan(
    413           cur_time + TimeDelta::FromSeconds(1)));
    414 }
    415 
    416 TEST_F(UmEvaluationContextTest,
    417        IsWallclockTimeGreaterThanSignalsTriggerReevaluation) {
    418   EXPECT_FALSE(eval_ctx_->IsWallclockTimeGreaterThan(
    419       fake_clock_.GetWallclockTime() + TimeDelta::FromSeconds(1)));
    420 
    421   // The "false" from IsWallclockTimeGreaterThan means that's not that timestamp
    422   // yet, so this should schedule a callback for when that happens.
    423   EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
    424 }
    425 
    426 TEST_F(UmEvaluationContextTest,
    427        IsMonotonicTimeGreaterThanSignalsTriggerReevaluation) {
    428   EXPECT_FALSE(eval_ctx_->IsMonotonicTimeGreaterThan(
    429       fake_clock_.GetMonotonicTime() + TimeDelta::FromSeconds(1)));
    430 
    431   // The "false" from IsMonotonicTimeGreaterThan means that's not that timestamp
    432   // yet, so this should schedule a callback for when that happens.
    433   EXPECT_TRUE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
    434 }
    435 
    436 TEST_F(UmEvaluationContextTest,
    437        IsWallclockTimeGreaterThanDoesntRecordPastTimestamps) {
    438   // IsWallclockTimeGreaterThan() should ignore timestamps on the past for
    439   // reevaluation.
    440   EXPECT_TRUE(eval_ctx_->IsWallclockTimeGreaterThan(
    441       fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(20)));
    442   EXPECT_TRUE(eval_ctx_->IsWallclockTimeGreaterThan(
    443       fake_clock_.GetWallclockTime() - TimeDelta::FromSeconds(1)));
    444 
    445   // Callback should not be scheduled.
    446   EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
    447 }
    448 
    449 TEST_F(UmEvaluationContextTest,
    450        IsMonotonicTimeGreaterThanDoesntRecordPastTimestamps) {
    451   // IsMonotonicTimeGreaterThan() should ignore timestamps on the past for
    452   // reevaluation.
    453   EXPECT_TRUE(eval_ctx_->IsMonotonicTimeGreaterThan(
    454       fake_clock_.GetMonotonicTime() - TimeDelta::FromSeconds(20)));
    455   EXPECT_TRUE(eval_ctx_->IsMonotonicTimeGreaterThan(
    456       fake_clock_.GetMonotonicTime() - TimeDelta::FromSeconds(1)));
    457 
    458   // Callback should not be scheduled.
    459   EXPECT_FALSE(eval_ctx_->RunOnValueChangeOrTimeout(Bind(&DoNothing)));
    460 }
    461 
    462 TEST_F(UmEvaluationContextTest, DumpContext) {
    463   // |fail_var_| yield "(no value)" since it is unset.
    464   eval_ctx_->GetValue(&fail_var_);
    465 
    466   // Check that this is included.
    467   fake_int_var_.reset(new int(42));
    468   eval_ctx_->GetValue(&fake_int_var_);
    469 
    470   // Check that double-quotes are escaped properly.
    471   fake_poll_var_.reset(new string("Hello \"world\"!"));
    472   eval_ctx_->GetValue(&fake_poll_var_);
    473 
    474   // Note that the variables are printed in alphabetical order. Also
    475   // see UmEvaluationContextText::SetUp() where the values used for
    476   // |evaluation_start_{monotonic,wallclock| are set.
    477   EXPECT_EQ("{\n"
    478             "   \"evaluation_start_monotonic\": \"4/22/2009 19:25:00 GMT\",\n"
    479             "   \"evaluation_start_wallclock\": \"3/2/2006 1:23:45 GMT\",\n"
    480             "   \"variables\": {\n"
    481             "      \"fail_var\": \"(no value)\",\n"
    482             "      \"fake_int\": \"42\",\n"
    483             "      \"fake_poll\": \"Hello \\\"world\\\"!\"\n"
    484             "   }\n"
    485             "}",
    486             eval_ctx_->DumpContext());
    487 }
    488 
    489 }  // namespace chromeos_update_manager
    490