// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "extensions/browser/api/idle/idle_api.h" #include #include #include #include "base/bind.h" #include "base/strings/string_number_conversions.h" #include "extensions/browser/api/idle/idle_api_constants.h" #include "extensions/browser/api/idle/idle_manager.h" #include "extensions/browser/api/idle/idle_manager_factory.h" #include "extensions/browser/api_unittest.h" #include "extensions/browser/event_router.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/unloaded_extension_reason.h" #include "extensions/common/api/idle.h" #include "extensions/common/extension.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; namespace idle = extensions::api::idle; namespace extensions { namespace { class MockEventDelegate : public IdleManager::EventDelegate { public: MockEventDelegate() {} ~MockEventDelegate() override {} MOCK_METHOD2(OnStateChanged, void(const std::string&, ui::IdleState)); void RegisterObserver(EventRouter::Observer* observer) override {} void UnregisterObserver(EventRouter::Observer* observer) override {} }; class TestIdleProvider : public IdleManager::IdleTimeProvider { public: TestIdleProvider(); ~TestIdleProvider() override; ui::IdleState CalculateIdleState(int idle_threshold) override; int CalculateIdleTime() override; bool CheckIdleStateIsLocked() override; void set_idle_time(int idle_time); void set_locked(bool locked); private: int idle_time_; bool locked_; }; TestIdleProvider::TestIdleProvider() : idle_time_(0), locked_(false) { } TestIdleProvider::~TestIdleProvider() { } ui::IdleState TestIdleProvider::CalculateIdleState(int idle_threshold) { if (locked_) { return ui::IDLE_STATE_LOCKED; } else if (idle_time_ >= idle_threshold) { return ui::IDLE_STATE_IDLE; } else { return ui::IDLE_STATE_ACTIVE; } } int TestIdleProvider::CalculateIdleTime() { return idle_time_; } bool TestIdleProvider::CheckIdleStateIsLocked() { return locked_; } void TestIdleProvider::set_idle_time(int idle_time) { idle_time_ = idle_time; } void TestIdleProvider::set_locked(bool locked) { locked_ = locked; } class ScopedListen { public: ScopedListen(IdleManager* idle_manager, const std::string& extension_id); ~ScopedListen(); private: IdleManager* idle_manager_; const std::string extension_id_; }; ScopedListen::ScopedListen(IdleManager* idle_manager, const std::string& extension_id) : idle_manager_(idle_manager), extension_id_(extension_id) { const EventListenerInfo details(idle::OnStateChanged::kEventName, extension_id_, GURL(), NULL); idle_manager_->OnListenerAdded(details); } ScopedListen::~ScopedListen() { const EventListenerInfo details(idle::OnStateChanged::kEventName, extension_id_, GURL(), NULL); idle_manager_->OnListenerRemoved(details); } std::unique_ptr IdleManagerTestFactory( content::BrowserContext* context) { return std::make_unique(context); } } // namespace class IdleTest : public ApiUnitTest { public: void SetUp() override; protected: IdleManager* idle_manager_; TestIdleProvider* idle_provider_; testing::StrictMock* event_delegate_; }; void IdleTest::SetUp() { ApiUnitTest::SetUp(); IdleManagerFactory::GetInstance()->SetTestingFactory( browser_context(), base::BindRepeating(&IdleManagerTestFactory)); idle_manager_ = IdleManagerFactory::GetForBrowserContext(browser_context()); idle_provider_ = new TestIdleProvider(); idle_manager_->SetIdleTimeProviderForTest( std::unique_ptr(idle_provider_)); event_delegate_ = new testing::StrictMock(); idle_manager_->SetEventDelegateForTest( std::unique_ptr(event_delegate_)); idle_manager_->Init(); } // Verifies that "locked" takes priority over "active". TEST_F(IdleTest, QueryLockedActive) { idle_provider_->set_locked(true); idle_provider_->set_idle_time(0); std::unique_ptr result( RunFunctionAndReturnValue(new IdleQueryStateFunction(), "[60]")); std::string idle_state; ASSERT_TRUE(result->GetAsString(&idle_state)); EXPECT_EQ("locked", idle_state); } // Verifies that "locked" takes priority over "idle". TEST_F(IdleTest, QueryLockedIdle) { idle_provider_->set_locked(true); idle_provider_->set_idle_time(INT_MAX); std::unique_ptr result( RunFunctionAndReturnValue(new IdleQueryStateFunction(), "[60]")); std::string idle_state; ASSERT_TRUE(result->GetAsString(&idle_state)); EXPECT_EQ("locked", idle_state); } // Verifies that any amount of idle time less than the detection interval // translates to a state of "active". TEST_F(IdleTest, QueryActive) { idle_provider_->set_locked(false); for (int time = 0; time < 60; ++time) { SCOPED_TRACE(time); idle_provider_->set_idle_time(time); std::unique_ptr result( RunFunctionAndReturnValue(new IdleQueryStateFunction(), "[60]")); std::string idle_state; ASSERT_TRUE(result->GetAsString(&idle_state)); EXPECT_EQ("active", idle_state); } } // Verifies that an idle time >= the detection interval returns the "idle" // state. TEST_F(IdleTest, QueryIdle) { idle_provider_->set_locked(false); for (int time = 80; time >= 60; --time) { SCOPED_TRACE(time); idle_provider_->set_idle_time(time); std::unique_ptr result( RunFunctionAndReturnValue(new IdleQueryStateFunction(), "[60]")); std::string idle_state; ASSERT_TRUE(result->GetAsString(&idle_state)); EXPECT_EQ("idle", idle_state); } } // Verifies that requesting a detection interval < 15 has the same effect as // passing in 15. TEST_F(IdleTest, QueryMinThreshold) { idle_provider_->set_locked(false); for (int threshold = 0; threshold < 20; ++threshold) { for (int time = 10; time < 60; ++time) { SCOPED_TRACE(threshold); SCOPED_TRACE(time); idle_provider_->set_idle_time(time); std::string args = "[" + base::NumberToString(threshold) + "]"; std::unique_ptr result( RunFunctionAndReturnValue(new IdleQueryStateFunction(), args)); std::string idle_state; ASSERT_TRUE(result->GetAsString(&idle_state)); int real_threshold = (threshold < 15) ? 15 : threshold; const char* expected = (time < real_threshold) ? "active" : "idle"; EXPECT_EQ(expected, idle_state); } } } // Verifies that passing in a detection interval > 4 hours has the same effect // as passing in 4 hours. TEST_F(IdleTest, QueryMaxThreshold) { idle_provider_->set_locked(false); const int kFourHoursInSeconds = 4 * 60 * 60; for (int threshold = kFourHoursInSeconds - 20; threshold < (kFourHoursInSeconds + 20); ++threshold) { for (int time = kFourHoursInSeconds - 30; time < kFourHoursInSeconds + 30; ++time) { SCOPED_TRACE(threshold); SCOPED_TRACE(time); idle_provider_->set_idle_time(time); std::string args = "[" + base::NumberToString(threshold) + "]"; std::unique_ptr result( RunFunctionAndReturnValue(new IdleQueryStateFunction(), args)); std::string idle_state; ASSERT_TRUE(result->GetAsString(&idle_state)); int real_threshold = (threshold > kFourHoursInSeconds) ? kFourHoursInSeconds : threshold; const char* expected = (time < real_threshold) ? "active" : "idle"; EXPECT_EQ(expected, idle_state); } } } // Verifies that transitioning from an active to idle state fires an "idle" // OnStateChanged event. TEST_F(IdleTest, ActiveToIdle) { ScopedListen listen_test(idle_manager_, "test"); idle_provider_->set_locked(false); for (int time = 0; time < 60; ++time) { SCOPED_TRACE(time); idle_provider_->set_idle_time(time); idle_manager_->UpdateIdleState(); } idle_provider_->set_idle_time(60); EXPECT_CALL(*event_delegate_, OnStateChanged("test", ui::IDLE_STATE_IDLE)); idle_manager_->UpdateIdleState(); testing::Mock::VerifyAndClearExpectations(event_delegate_); for (int time = 61; time < 75; ++time) { SCOPED_TRACE(time); idle_provider_->set_idle_time(time); idle_manager_->UpdateIdleState(); } } // Verifies that locking an active system generates a "locked" event. TEST_F(IdleTest, ActiveToLocked) { ScopedListen listen_test(idle_manager_, "test"); idle_provider_->set_locked(true); idle_provider_->set_idle_time(5); EXPECT_CALL(*event_delegate_, OnStateChanged("test", ui::IDLE_STATE_LOCKED)); idle_manager_->UpdateIdleState(); } // Verifies that transitioning from an idle to active state generates an // "active" event. TEST_F(IdleTest, IdleToActive) { ScopedListen listen_test(idle_manager_, "test"); idle_provider_->set_locked(false); idle_provider_->set_idle_time(75); EXPECT_CALL(*event_delegate_, OnStateChanged("test", ui::IDLE_STATE_IDLE)); idle_manager_->UpdateIdleState(); testing::Mock::VerifyAndClearExpectations(event_delegate_); idle_provider_->set_idle_time(0); EXPECT_CALL(*event_delegate_, OnStateChanged("test", ui::IDLE_STATE_ACTIVE)); idle_manager_->UpdateIdleState(); } // Verifies that locking an idle system generates a "locked" event. TEST_F(IdleTest, IdleToLocked) { ScopedListen listen_test(idle_manager_, "test"); idle_provider_->set_locked(false); idle_provider_->set_idle_time(75); EXPECT_CALL(*event_delegate_, OnStateChanged("test", ui::IDLE_STATE_IDLE)); idle_manager_->UpdateIdleState(); testing::Mock::VerifyAndClearExpectations(event_delegate_); idle_provider_->set_locked(true); EXPECT_CALL(*event_delegate_, OnStateChanged("test", ui::IDLE_STATE_LOCKED)); idle_manager_->UpdateIdleState(); } // Verifies that unlocking an active system generates an "active" event. TEST_F(IdleTest, LockedToActive) { ScopedListen listen_test(idle_manager_, "test"); idle_provider_->set_locked(true); idle_provider_->set_idle_time(0); EXPECT_CALL(*event_delegate_, OnStateChanged("test", ui::IDLE_STATE_LOCKED)); idle_manager_->UpdateIdleState(); idle_provider_->set_locked(false); idle_provider_->set_idle_time(5); EXPECT_CALL(*event_delegate_, OnStateChanged("test", ui::IDLE_STATE_ACTIVE)); idle_manager_->UpdateIdleState(); } // Verifies that unlocking an inactive system generates an "idle" event. TEST_F(IdleTest, LockedToIdle) { ScopedListen listen_test(idle_manager_, "test"); idle_provider_->set_locked(true); idle_provider_->set_idle_time(75); EXPECT_CALL(*event_delegate_, OnStateChanged("test", ui::IDLE_STATE_LOCKED)); idle_manager_->UpdateIdleState(); testing::Mock::VerifyAndClearExpectations(event_delegate_); idle_provider_->set_locked(false); EXPECT_CALL(*event_delegate_, OnStateChanged("test", ui::IDLE_STATE_IDLE)); idle_manager_->UpdateIdleState(); } // Verifies that events are routed to extensions that have one or more listeners // in scope. TEST_F(IdleTest, MultipleExtensions) { ScopedListen listen_1(idle_manager_, "1"); ScopedListen listen_2(idle_manager_, "2"); idle_provider_->set_locked(true); EXPECT_CALL(*event_delegate_, OnStateChanged("1", ui::IDLE_STATE_LOCKED)); EXPECT_CALL(*event_delegate_, OnStateChanged("2", ui::IDLE_STATE_LOCKED)); idle_manager_->UpdateIdleState(); testing::Mock::VerifyAndClearExpectations(event_delegate_); { ScopedListen listen_2prime(idle_manager_, "2"); ScopedListen listen_3(idle_manager_, "3"); idle_provider_->set_locked(false); EXPECT_CALL(*event_delegate_, OnStateChanged("1", ui::IDLE_STATE_ACTIVE)); EXPECT_CALL(*event_delegate_, OnStateChanged("2", ui::IDLE_STATE_ACTIVE)); EXPECT_CALL(*event_delegate_, OnStateChanged("3", ui::IDLE_STATE_ACTIVE)); idle_manager_->UpdateIdleState(); testing::Mock::VerifyAndClearExpectations(event_delegate_); } idle_provider_->set_locked(true); EXPECT_CALL(*event_delegate_, OnStateChanged("1", ui::IDLE_STATE_LOCKED)); EXPECT_CALL(*event_delegate_, OnStateChanged("2", ui::IDLE_STATE_LOCKED)); idle_manager_->UpdateIdleState(); } // Verifies that setDetectionInterval changes the detection interval from the // default of 60 seconds, and that the call only affects a single extension's // IdleMonitor. TEST_F(IdleTest, SetDetectionInterval) { ScopedListen listen_default(idle_manager_, "default"); ScopedListen listen_extension(idle_manager_, extension()->id()); std::unique_ptr result45(RunFunctionAndReturnValue( new IdleSetDetectionIntervalFunction(), "[45]")); idle_provider_->set_locked(false); idle_provider_->set_idle_time(44); idle_manager_->UpdateIdleState(); idle_provider_->set_idle_time(45); EXPECT_CALL(*event_delegate_, OnStateChanged(extension()->id(), ui::IDLE_STATE_IDLE)); idle_manager_->UpdateIdleState(); // Verify that the expectation has been fulfilled before incrementing the // time again. testing::Mock::VerifyAndClearExpectations(event_delegate_); idle_provider_->set_idle_time(60); EXPECT_CALL(*event_delegate_, OnStateChanged("default", ui::IDLE_STATE_IDLE)); idle_manager_->UpdateIdleState(); } // Verifies that setting the detection interval before creating the listener // works correctly. TEST_F(IdleTest, SetDetectionIntervalBeforeListener) { std::unique_ptr result45(RunFunctionAndReturnValue( new IdleSetDetectionIntervalFunction(), "[45]")); ScopedListen listen_extension(idle_manager_, extension()->id()); idle_provider_->set_locked(false); idle_provider_->set_idle_time(44); idle_manager_->UpdateIdleState(); idle_provider_->set_idle_time(45); EXPECT_CALL(*event_delegate_, OnStateChanged(extension()->id(), ui::IDLE_STATE_IDLE)); idle_manager_->UpdateIdleState(); } // Verifies that setting a detection interval above the maximum value results // in an interval of 4 hours. TEST_F(IdleTest, SetDetectionIntervalMaximum) { ScopedListen listen_extension(idle_manager_, extension()->id()); std::unique_ptr result( RunFunctionAndReturnValue(new IdleSetDetectionIntervalFunction(), "[18000]")); // five hours in seconds idle_provider_->set_locked(false); idle_provider_->set_idle_time(4 * 60 * 60 - 1); idle_manager_->UpdateIdleState(); idle_provider_->set_idle_time(4 * 60 * 60); EXPECT_CALL(*event_delegate_, OnStateChanged(extension()->id(), ui::IDLE_STATE_IDLE)); idle_manager_->UpdateIdleState(); } // Verifies that setting a detection interval below the minimum value results // in an interval of 15 seconds. TEST_F(IdleTest, SetDetectionIntervalMinimum) { ScopedListen listen_extension(idle_manager_, extension()->id()); std::unique_ptr result(RunFunctionAndReturnValue( new IdleSetDetectionIntervalFunction(), "[10]")); idle_provider_->set_locked(false); idle_provider_->set_idle_time(14); idle_manager_->UpdateIdleState(); idle_provider_->set_idle_time(15); EXPECT_CALL(*event_delegate_, OnStateChanged(extension()->id(), ui::IDLE_STATE_IDLE)); idle_manager_->UpdateIdleState(); } // Verifies that an extension's detection interval is discarded when it unloads. TEST_F(IdleTest, UnloadCleanup) { { ScopedListen listen(idle_manager_, extension()->id()); std::unique_ptr result45(RunFunctionAndReturnValue( new IdleSetDetectionIntervalFunction(), "[15]")); } // Listener count dropping to zero does not reset threshold. { ScopedListen listen(idle_manager_, extension()->id()); idle_provider_->set_idle_time(16); EXPECT_CALL(*event_delegate_, OnStateChanged(extension()->id(), ui::IDLE_STATE_IDLE)); idle_manager_->UpdateIdleState(); testing::Mock::VerifyAndClearExpectations(event_delegate_); } // Threshold will reset after unload (and listen count == 0) ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context()); registry->TriggerOnUnloaded(extension(), UnloadedExtensionReason::UNINSTALL); { ScopedListen listen(idle_manager_, extension()->id()); idle_manager_->UpdateIdleState(); testing::Mock::VerifyAndClearExpectations(event_delegate_); idle_provider_->set_idle_time(61); EXPECT_CALL(*event_delegate_, OnStateChanged(extension()->id(), ui::IDLE_STATE_IDLE)); idle_manager_->UpdateIdleState(); } } // Verifies that unloading an extension with no listeners or threshold works. TEST_F(IdleTest, UnloadOnly) { ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context()); registry->TriggerOnUnloaded(extension(), UnloadedExtensionReason::UNINSTALL); } // Verifies that its ok for the unload notification to happen before all the // listener removals. TEST_F(IdleTest, UnloadWhileListening) { ScopedListen listen(idle_manager_, extension()->id()); ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context()); registry->TriggerOnUnloaded(extension(), UnloadedExtensionReason::UNINSTALL); } // Verifies that re-adding a listener after a state change doesn't immediately // fire a change event. Regression test for http://crbug.com/366580. TEST_F(IdleTest, ReAddListener) { idle_provider_->set_locked(false); { // Fire idle event. ScopedListen listen(idle_manager_, "test"); idle_provider_->set_idle_time(60); EXPECT_CALL(*event_delegate_, OnStateChanged("test", ui::IDLE_STATE_IDLE)); idle_manager_->UpdateIdleState(); testing::Mock::VerifyAndClearExpectations(event_delegate_); } // Trigger active. idle_provider_->set_idle_time(0); idle_manager_->UpdateIdleState(); { // Nothing should have fired, the listener wasn't added until afterward. ScopedListen listen(idle_manager_, "test"); idle_manager_->UpdateIdleState(); testing::Mock::VerifyAndClearExpectations(event_delegate_); } } } // namespace extensions