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