idle_api_unittest.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/extensions/api/idle/idle_api.h" 6 7#include <limits.h> 8#include <string> 9 10#include "base/strings/string_number_conversions.h" 11#include "chrome/browser/extensions/api/idle/idle_api_constants.h" 12#include "chrome/browser/extensions/api/idle/idle_manager.h" 13#include "chrome/browser/extensions/api/idle/idle_manager_factory.h" 14#include "chrome/browser/extensions/event_router.h" 15#include "chrome/browser/extensions/extension_function_test_utils.h" 16#include "chrome/common/chrome_notification_types.h" 17#include "chrome/common/extensions/extension.h" 18#include "chrome/common/extensions/extension_constants.h" 19#include "chrome/test/base/browser_with_test_window_test.h" 20#include "content/public/browser/notification_details.h" 21#include "content/public/browser/notification_source.h" 22#include "testing/gmock/include/gmock/gmock.h" 23#include "testing/gtest/include/gtest/gtest.h" 24 25using ::testing::_; 26 27namespace utils = extension_function_test_utils; 28 29namespace extensions { 30 31namespace { 32 33class MockEventDelegate : public IdleManager::EventDelegate { 34 public: 35 MockEventDelegate() {} 36 virtual ~MockEventDelegate() {} 37 MOCK_METHOD2(OnStateChanged, void(const std::string&, IdleState)); 38 virtual void RegisterObserver(EventRouter::Observer* observer) {} 39 virtual void UnregisterObserver(EventRouter::Observer* observer) {} 40}; 41 42class TestIdleProvider : public IdleManager::IdleTimeProvider { 43 public: 44 TestIdleProvider(); 45 virtual ~TestIdleProvider(); 46 virtual void CalculateIdleState(int idle_threshold, 47 IdleCallback notify) OVERRIDE; 48 virtual void CalculateIdleTime(IdleTimeCallback notify) OVERRIDE; 49 virtual bool CheckIdleStateIsLocked() OVERRIDE; 50 51 void set_idle_time(int idle_time); 52 void set_locked(bool locked); 53 54 private: 55 int idle_time_; 56 bool locked_; 57}; 58 59TestIdleProvider::TestIdleProvider() 60 : idle_time_(0), 61 locked_(false) { 62} 63 64TestIdleProvider::~TestIdleProvider() { 65} 66 67void TestIdleProvider::CalculateIdleState(int idle_threshold, 68 IdleCallback notify) { 69 if (locked_) { 70 notify.Run(IDLE_STATE_LOCKED); 71 } else { 72 if (idle_time_ >= idle_threshold) { 73 notify.Run(IDLE_STATE_IDLE); 74 } else { 75 notify.Run(IDLE_STATE_ACTIVE); 76 } 77 } 78} 79 80void TestIdleProvider::CalculateIdleTime(IdleTimeCallback notify) { 81 notify.Run(idle_time_); 82} 83 84bool TestIdleProvider::CheckIdleStateIsLocked() { 85 return locked_; 86} 87 88void TestIdleProvider::set_idle_time(int idle_time) { 89 idle_time_ = idle_time; 90} 91 92void TestIdleProvider::set_locked(bool locked) { 93 locked_ = locked; 94} 95 96class ScopedListen { 97 public: 98 ScopedListen(IdleManager* idle_manager, const std::string& extension_id); 99 ~ScopedListen(); 100 101 private: 102 IdleManager* idle_manager_; 103 const std::string extension_id_; 104}; 105 106ScopedListen::ScopedListen(IdleManager* idle_manager, 107 const std::string& extension_id) 108 : idle_manager_(idle_manager), 109 extension_id_(extension_id) { 110 const EventListenerInfo details(idle_api_constants::kOnStateChanged, 111 extension_id_); 112 idle_manager_->OnListenerAdded(details); 113} 114 115ScopedListen::~ScopedListen() { 116 const EventListenerInfo details(idle_api_constants::kOnStateChanged, 117 extension_id_); 118 idle_manager_->OnListenerRemoved(details); 119} 120 121ProfileKeyedService* IdleManagerTestFactory(content::BrowserContext* profile) { 122 return new IdleManager(static_cast<Profile*>(profile)); 123} 124 125} // namespace 126 127class IdleTest : public BrowserWithTestWindowTest { 128 public: 129 virtual void SetUp() OVERRIDE; 130 131 protected: 132 base::Value* RunFunctionWithExtension( 133 UIThreadExtensionFunction* function, const std::string& args); 134 135 IdleManager* idle_manager_; 136 TestIdleProvider* idle_provider_; 137 testing::StrictMock<MockEventDelegate>* event_delegate_; 138 scoped_refptr<extensions::Extension> extension_; 139}; 140 141void IdleTest::SetUp() { 142 BrowserWithTestWindowTest::SetUp(); 143 144 IdleManagerFactory::GetInstance()->SetTestingFactory(browser()->profile(), 145 &IdleManagerTestFactory); 146 idle_manager_ = IdleManagerFactory::GetForProfile(browser()->profile()); 147 148 extension_ = utils::CreateEmptyExtensionWithLocation( 149 extensions::Manifest::UNPACKED); 150 151 idle_provider_ = new TestIdleProvider(); 152 idle_manager_->SetIdleTimeProviderForTest( 153 scoped_ptr<IdleManager::IdleTimeProvider>(idle_provider_).Pass()); 154 event_delegate_ = new testing::StrictMock<MockEventDelegate>(); 155 idle_manager_->SetEventDelegateForTest( 156 scoped_ptr<IdleManager::EventDelegate>(event_delegate_).Pass()); 157 idle_manager_->Init(); 158} 159 160base::Value* IdleTest::RunFunctionWithExtension( 161 UIThreadExtensionFunction* function, const std::string& args) { 162 function->set_extension(extension_.get()); 163 return utils::RunFunctionAndReturnSingleResult(function, args, browser()); 164} 165 166// Verifies that "locked" takes priority over "active". 167TEST_F(IdleTest, QueryLockedActive) { 168 idle_provider_->set_locked(true); 169 idle_provider_->set_idle_time(0); 170 171 scoped_ptr<base::Value> result(RunFunctionWithExtension( 172 new IdleQueryStateFunction(), 173 "[60]")); 174 175 std::string idle_state; 176 ASSERT_TRUE(result->GetAsString(&idle_state)); 177 EXPECT_EQ("locked", idle_state); 178} 179 180// Verifies that "locked" takes priority over "idle". 181TEST_F(IdleTest, QueryLockedIdle) { 182 idle_provider_->set_locked(true); 183 idle_provider_->set_idle_time(INT_MAX); 184 185 scoped_ptr<base::Value> result(RunFunctionWithExtension( 186 new IdleQueryStateFunction(), 187 "[60]")); 188 189 std::string idle_state; 190 ASSERT_TRUE(result->GetAsString(&idle_state)); 191 EXPECT_EQ("locked", idle_state); 192} 193 194// Verifies that any amount of idle time less than the detection interval 195// translates to a state of "active". 196TEST_F(IdleTest, QueryActive) { 197 idle_provider_->set_locked(false); 198 199 for (int time = 0; time < 60; ++time) { 200 SCOPED_TRACE(time); 201 idle_provider_->set_idle_time(time); 202 203 scoped_ptr<base::Value> result(RunFunctionWithExtension( 204 new IdleQueryStateFunction(), 205 "[60]")); 206 207 std::string idle_state; 208 ASSERT_TRUE(result->GetAsString(&idle_state)); 209 EXPECT_EQ("active", idle_state); 210 } 211} 212 213// Verifies that an idle time >= the detection interval returns the "idle" 214// state. 215TEST_F(IdleTest, QueryIdle) { 216 idle_provider_->set_locked(false); 217 218 for (int time = 80; time >= 60; --time) { 219 SCOPED_TRACE(time); 220 idle_provider_->set_idle_time(time); 221 222 scoped_ptr<base::Value> result(RunFunctionWithExtension( 223 new IdleQueryStateFunction(), 224 "[60]")); 225 226 std::string idle_state; 227 ASSERT_TRUE(result->GetAsString(&idle_state)); 228 EXPECT_EQ("idle", idle_state); 229 } 230} 231 232// Verifies that requesting a detection interval < 15 has the same effect as 233// passing in 15. 234TEST_F(IdleTest, QueryMinThreshold) { 235 idle_provider_->set_locked(false); 236 237 for (int threshold = 0; threshold < 20; ++threshold) { 238 for (int time = 10; time < 60; ++time) { 239 SCOPED_TRACE(threshold); 240 SCOPED_TRACE(time); 241 idle_provider_->set_idle_time(time); 242 243 std::string args = "[" + base::IntToString(threshold) + "]"; 244 scoped_ptr<base::Value> result(RunFunctionWithExtension( 245 new IdleQueryStateFunction(), args)); 246 247 std::string idle_state; 248 ASSERT_TRUE(result->GetAsString(&idle_state)); 249 250 int real_threshold = (threshold < 15) ? 15 : threshold; 251 const char* expected = (time < real_threshold) ? "active" : "idle"; 252 EXPECT_EQ(expected, idle_state); 253 } 254 } 255} 256 257// Verifies that passing in a detection interval > 4 hours has the same effect 258// as passing in 4 hours. 259TEST_F(IdleTest, QueryMaxThreshold) { 260 idle_provider_->set_locked(false); 261 262 const int kFourHoursInSeconds = 4*60*60; 263 264 for (int threshold = kFourHoursInSeconds - 20; 265 threshold < (kFourHoursInSeconds + 20); ++threshold) { 266 for (int time = kFourHoursInSeconds - 30; time < kFourHoursInSeconds + 30; 267 ++time) { 268 SCOPED_TRACE(threshold); 269 SCOPED_TRACE(time); 270 idle_provider_->set_idle_time(time); 271 272 std::string args = "[" + base::IntToString(threshold) + "]"; 273 scoped_ptr<base::Value> result(RunFunctionWithExtension( 274 new IdleQueryStateFunction(), args)); 275 276 std::string idle_state; 277 ASSERT_TRUE(result->GetAsString(&idle_state)); 278 279 int real_threshold = (threshold > kFourHoursInSeconds) ? 280 kFourHoursInSeconds : threshold; 281 const char* expected = (time < real_threshold) ? "active" : "idle"; 282 EXPECT_EQ(expected, idle_state); 283 } 284 } 285} 286 287// Verifies that transitioning from an active to idle state fires an "idle" 288// OnStateChanged event. 289TEST_F(IdleTest, ActiveToIdle) { 290 ScopedListen listen_test(idle_manager_, "test"); 291 292 idle_provider_->set_locked(false); 293 294 for (int time = 0; time < 60; ++time) { 295 SCOPED_TRACE(time); 296 idle_provider_->set_idle_time(time); 297 298 idle_manager_->UpdateIdleState(); 299 } 300 301 idle_provider_->set_idle_time(60); 302 303 EXPECT_CALL(*event_delegate_, OnStateChanged("test", IDLE_STATE_IDLE)); 304 idle_manager_->UpdateIdleState(); 305 testing::Mock::VerifyAndClearExpectations(event_delegate_); 306 307 for (int time = 61; time < 75; ++time) { 308 SCOPED_TRACE(time); 309 idle_provider_->set_idle_time(time); 310 idle_manager_->UpdateIdleState(); 311 } 312} 313 314// Verifies that locking an active system generates a "locked" event. 315TEST_F(IdleTest, ActiveToLocked) { 316 ScopedListen listen_test(idle_manager_, "test"); 317 318 idle_provider_->set_locked(true); 319 idle_provider_->set_idle_time(5); 320 321 EXPECT_CALL(*event_delegate_, OnStateChanged("test", IDLE_STATE_LOCKED)); 322 idle_manager_->UpdateIdleState(); 323} 324 325// Verifies that transitioning from an idle to active state generates an 326// "active" event. 327TEST_F(IdleTest, IdleToActive) { 328 ScopedListen listen_test(idle_manager_, "test"); 329 330 idle_provider_->set_locked(false); 331 idle_provider_->set_idle_time(75); 332 EXPECT_CALL(*event_delegate_, OnStateChanged("test", IDLE_STATE_IDLE)); 333 idle_manager_->UpdateIdleState(); 334 testing::Mock::VerifyAndClearExpectations(event_delegate_); 335 336 idle_provider_->set_idle_time(0); 337 EXPECT_CALL(*event_delegate_, OnStateChanged("test", IDLE_STATE_ACTIVE)); 338 idle_manager_->UpdateIdleState(); 339} 340 341// Verifies that locking an idle system generates a "locked" event. 342TEST_F(IdleTest, IdleToLocked) { 343 ScopedListen listen_test(idle_manager_, "test"); 344 345 idle_provider_->set_locked(false); 346 idle_provider_->set_idle_time(75); 347 348 EXPECT_CALL(*event_delegate_, OnStateChanged("test", IDLE_STATE_IDLE)); 349 idle_manager_->UpdateIdleState(); 350 testing::Mock::VerifyAndClearExpectations(event_delegate_); 351 352 idle_provider_->set_locked(true); 353 EXPECT_CALL(*event_delegate_, OnStateChanged("test", IDLE_STATE_LOCKED)); 354 idle_manager_->UpdateIdleState(); 355} 356 357// Verifies that unlocking an active system generates an "active" event. 358TEST_F(IdleTest, LockedToActive) { 359 ScopedListen listen_test(idle_manager_, "test"); 360 361 idle_provider_->set_locked(true); 362 idle_provider_->set_idle_time(0); 363 364 EXPECT_CALL(*event_delegate_, OnStateChanged("test", IDLE_STATE_LOCKED)); 365 idle_manager_->UpdateIdleState(); 366 367 idle_provider_->set_locked(false); 368 idle_provider_->set_idle_time(5); 369 EXPECT_CALL(*event_delegate_, OnStateChanged("test", IDLE_STATE_ACTIVE)); 370 idle_manager_->UpdateIdleState(); 371} 372 373// Verifies that unlocking an inactive system generates an "idle" event. 374TEST_F(IdleTest, LockedToIdle) { 375 ScopedListen listen_test(idle_manager_, "test"); 376 377 idle_provider_->set_locked(true); 378 idle_provider_->set_idle_time(75); 379 EXPECT_CALL(*event_delegate_, OnStateChanged("test", IDLE_STATE_LOCKED)); 380 idle_manager_->UpdateIdleState(); 381 testing::Mock::VerifyAndClearExpectations(event_delegate_); 382 383 idle_provider_->set_locked(false); 384 EXPECT_CALL(*event_delegate_, OnStateChanged("test", IDLE_STATE_IDLE)); 385 idle_manager_->UpdateIdleState(); 386} 387 388// Verifies that events are routed to extensions that have one or more listeners 389// in scope. 390TEST_F(IdleTest, MultipleExtensions) { 391 ScopedListen listen_1(idle_manager_, "1"); 392 ScopedListen listen_2(idle_manager_, "2"); 393 394 idle_provider_->set_locked(true); 395 EXPECT_CALL(*event_delegate_, OnStateChanged("1", IDLE_STATE_LOCKED)); 396 EXPECT_CALL(*event_delegate_, OnStateChanged("2", IDLE_STATE_LOCKED)); 397 idle_manager_->UpdateIdleState(); 398 testing::Mock::VerifyAndClearExpectations(event_delegate_); 399 400 { 401 ScopedListen listen_2prime(idle_manager_, "2"); 402 ScopedListen listen_3(idle_manager_, "3"); 403 idle_provider_->set_locked(false); 404 EXPECT_CALL(*event_delegate_, OnStateChanged("1", IDLE_STATE_ACTIVE)); 405 EXPECT_CALL(*event_delegate_, OnStateChanged("2", IDLE_STATE_ACTIVE)); 406 EXPECT_CALL(*event_delegate_, OnStateChanged("3", IDLE_STATE_ACTIVE)); 407 idle_manager_->UpdateIdleState(); 408 testing::Mock::VerifyAndClearExpectations(event_delegate_); 409 } 410 411 idle_provider_->set_locked(true); 412 EXPECT_CALL(*event_delegate_, OnStateChanged("1", IDLE_STATE_LOCKED)); 413 EXPECT_CALL(*event_delegate_, OnStateChanged("2", IDLE_STATE_LOCKED)); 414 idle_manager_->UpdateIdleState(); 415} 416 417// Verifies that setDetectionInterval changes the detection interval from the 418// default of 60 seconds, and that the call only affects a single extension's 419// IdleMonitor. 420TEST_F(IdleTest, SetDetectionInterval) { 421 ScopedListen listen_default(idle_manager_, "default"); 422 ScopedListen listen_extension(idle_manager_, extension_->id()); 423 424 scoped_ptr<base::Value> result45(RunFunctionWithExtension( 425 new IdleSetDetectionIntervalFunction(), 426 "[45]")); 427 428 idle_provider_->set_locked(false); 429 idle_provider_->set_idle_time(44); 430 idle_manager_->UpdateIdleState(); 431 432 idle_provider_->set_idle_time(45); 433 EXPECT_CALL(*event_delegate_, 434 OnStateChanged(extension_->id(), IDLE_STATE_IDLE)); 435 idle_manager_->UpdateIdleState(); 436 // Verify that the expectation has been fulfilled before incrementing the 437 // time again. 438 testing::Mock::VerifyAndClearExpectations(event_delegate_); 439 440 idle_provider_->set_idle_time(60); 441 EXPECT_CALL(*event_delegate_, OnStateChanged("default", IDLE_STATE_IDLE)); 442 idle_manager_->UpdateIdleState(); 443} 444 445// Verifies that setting the detection interval before creating the listener 446// works correctly. 447TEST_F(IdleTest, SetDetectionIntervalBeforeListener) { 448 scoped_ptr<base::Value> result45(RunFunctionWithExtension( 449 new IdleSetDetectionIntervalFunction(), 450 "[45]")); 451 452 ScopedListen listen_extension(idle_manager_, extension_->id()); 453 454 idle_provider_->set_locked(false); 455 idle_provider_->set_idle_time(44); 456 idle_manager_->UpdateIdleState(); 457 458 idle_provider_->set_idle_time(45); 459 EXPECT_CALL(*event_delegate_, 460 OnStateChanged(extension_->id(), IDLE_STATE_IDLE)); 461 idle_manager_->UpdateIdleState(); 462} 463 464// Verifies that setting a detection interval above the maximum value results 465// in an interval of 4 hours. 466TEST_F(IdleTest, SetDetectionIntervalMaximum) { 467 ScopedListen listen_extension(idle_manager_, extension_->id()); 468 469 scoped_ptr<base::Value> result(RunFunctionWithExtension( 470 new IdleSetDetectionIntervalFunction(), 471 "[18000]")); // five hours in seconds 472 473 idle_provider_->set_locked(false); 474 idle_provider_->set_idle_time(4*60*60 - 1); 475 idle_manager_->UpdateIdleState(); 476 477 idle_provider_->set_idle_time(4*60*60); 478 EXPECT_CALL(*event_delegate_, 479 OnStateChanged(extension_->id(), IDLE_STATE_IDLE)); 480 idle_manager_->UpdateIdleState(); 481} 482 483// Verifies that setting a detection interval below the minimum value results 484// in an interval of 15 seconds. 485TEST_F(IdleTest, SetDetectionIntervalMinimum) { 486 ScopedListen listen_extension(idle_manager_, extension_->id()); 487 488 scoped_ptr<base::Value> result(RunFunctionWithExtension( 489 new IdleSetDetectionIntervalFunction(), 490 "[10]")); 491 492 idle_provider_->set_locked(false); 493 idle_provider_->set_idle_time(14); 494 idle_manager_->UpdateIdleState(); 495 496 idle_provider_->set_idle_time(15); 497 EXPECT_CALL(*event_delegate_, 498 OnStateChanged(extension_->id(), IDLE_STATE_IDLE)); 499 idle_manager_->UpdateIdleState(); 500} 501 502// Verifies that an extension's detection interval is discarded when it unloads. 503TEST_F(IdleTest, UnloadCleanup) { 504 { 505 ScopedListen listen(idle_manager_, extension_->id()); 506 507 scoped_ptr<base::Value> result45(RunFunctionWithExtension( 508 new IdleSetDetectionIntervalFunction(), 509 "[15]")); 510 } 511 512 // Listener count dropping to zero does not reset threshold. 513 514 { 515 ScopedListen listen(idle_manager_, extension_->id()); 516 idle_provider_->set_idle_time(16); 517 EXPECT_CALL(*event_delegate_, 518 OnStateChanged(extension_->id(), IDLE_STATE_IDLE)); 519 idle_manager_->UpdateIdleState(); 520 testing::Mock::VerifyAndClearExpectations(event_delegate_); 521 } 522 523 // Threshold will reset after unload (and listen count == 0) 524 UnloadedExtensionInfo details(extension_, 525 extension_misc::UNLOAD_REASON_UNINSTALL); 526 idle_manager_->Observe( 527 chrome::NOTIFICATION_EXTENSION_UNLOADED, 528 content::Source<Profile>(browser()->profile()), 529 content::Details<UnloadedExtensionInfo>(&details)); 530 531 { 532 ScopedListen listen(idle_manager_, extension_->id()); 533 idle_manager_->UpdateIdleState(); 534 testing::Mock::VerifyAndClearExpectations(event_delegate_); 535 536 idle_provider_->set_idle_time(61); 537 EXPECT_CALL(*event_delegate_, 538 OnStateChanged(extension_->id(), IDLE_STATE_IDLE)); 539 idle_manager_->UpdateIdleState(); 540 } 541} 542 543// Verifies that unloading an extension with no listeners or threshold works. 544TEST_F(IdleTest, UnloadOnly) { 545 UnloadedExtensionInfo details(extension_, 546 extension_misc::UNLOAD_REASON_UNINSTALL); 547 idle_manager_->Observe( 548 chrome::NOTIFICATION_EXTENSION_UNLOADED, 549 content::Source<Profile>(browser()->profile()), 550 content::Details<UnloadedExtensionInfo>(&details)); 551} 552 553// Verifies that its ok for the unload notification to happen before all the 554// listener removals. 555TEST_F(IdleTest, UnloadWhileListening) { 556 ScopedListen listen(idle_manager_, extension_->id()); 557 UnloadedExtensionInfo details(extension_, 558 extension_misc::UNLOAD_REASON_UNINSTALL); 559 idle_manager_->Observe( 560 chrome::NOTIFICATION_EXTENSION_UNLOADED, 561 content::Source<Profile>(browser()->profile()), 562 content::Details<UnloadedExtensionInfo>(&details)); 563} 564 565} // namespace extensions 566