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