blob: c94714da12421efa7be728bcdcbcfe7933f9a27c [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS 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 "power_manager/powerd/policy/input_controller.h"
#include <stdint.h>
#include <string>
#include <vector>
#include <base/format_macros.h>
#include <base/strings/stringprintf.h>
#include <chromeos/dbus/service_constants.h>
#include <gtest/gtest.h>
#include "power_manager/common/action_recorder.h"
#include "power_manager/common/clock.h"
#include "power_manager/common/dbus_sender_stub.h"
#include "power_manager/common/fake_prefs.h"
#include "power_manager/common/power_constants.h"
#include "power_manager/powerd/system/display/display_info.h"
#include "power_manager/powerd/system/display/display_watcher_stub.h"
#include "power_manager/powerd/system/input_watcher_stub.h"
#include "power_manager/proto_bindings/input_event.pb.h"
namespace power_manager {
namespace policy {
namespace {
const char kNoActions[] = "";
const char kLidClosed[] = "lid_closed";
const char kLidOpened[] = "lid_opened";
const char kPowerButtonDown[] = "power_down";
const char kPowerButtonUp[] = "power_up";
const char kDeferInactivity[] = "defer_inactivity";
const char kShutDown[] = "shut_down";
const char kMissingPowerButtonAcknowledgment[] = "missing_power_button_ack";
const char kHoverOn[] = "hover_on";
const char kHoverOff[] = "hover_off";
std::string GetAcknowledgmentDelayAction(base::TimeDelta delay) {
return base::StringPrintf("power_button_ack_delay(%" PRId64 ")",
delay.InMilliseconds());
}
class TestInputControllerDelegate : public InputController::Delegate,
public ActionRecorder {
public:
TestInputControllerDelegate() {}
virtual ~TestInputControllerDelegate() {}
// InputController::Delegate implementation:
void HandleLidClosed() override {
AppendAction(kLidClosed);
}
void HandleLidOpened() override {
AppendAction(kLidOpened);
}
void HandlePowerButtonEvent(ButtonState state) override {
AppendAction(state == BUTTON_DOWN ? kPowerButtonDown : kPowerButtonUp);
}
void DeferInactivityTimeoutForVT2() override {
AppendAction(kDeferInactivity);
}
void ShutDownForPowerButtonWithNoDisplay() override {
AppendAction(kShutDown);
}
void HandleMissingPowerButtonAcknowledgment() override {
AppendAction(kMissingPowerButtonAcknowledgment);
}
void ReportPowerButtonAcknowledgmentDelay(base::TimeDelta delay) override {
AppendAction(GetAcknowledgmentDelayAction(delay));
}
void HandleHoverStateChanged(bool hovering) override {
AppendAction(hovering ? kHoverOn : kHoverOff);
}
private:
DISALLOW_COPY_AND_ASSIGN(TestInputControllerDelegate);
};
} // namespace
class InputControllerTest : public ::testing::Test {
public:
InputControllerTest() {
controller_.clock_for_testing()->set_current_time_for_testing(
base::TimeTicks::FromInternalValue(1000));
}
virtual ~InputControllerTest() {}
protected:
// Initializes |controller_|.
void Init() {
controller_.Init(
&input_watcher_, &delegate_, &display_watcher_, &dbus_sender_, &prefs_);
}
// Tests that one InputEvent D-Bus signal has been sent and returns the
// signal's |type| field.
int GetInputEventSignalType() {
InputEvent proto;
EXPECT_EQ(1, dbus_sender_.num_sent_signals());
EXPECT_TRUE(dbus_sender_.GetSentSignal(0, kInputEventSignal, &proto));
return proto.type();
}
// Tests that one InputEvent D-Bus signal has been sent and returns the
// signal's |timestamp| field.
int64_t GetInputEventSignalTimestamp() {
InputEvent proto;
EXPECT_EQ(1, dbus_sender_.num_sent_signals());
EXPECT_TRUE(dbus_sender_.GetSentSignal(0, kInputEventSignal, &proto));
return proto.timestamp();
}
// Returns the current (fake) time.
base::TimeTicks Now() {
return controller_.clock_for_testing()->GetCurrentTime();
}
// Advances the current time by |interval|.
void AdvanceTime(const base::TimeDelta& interval) {
controller_.clock_for_testing()->set_current_time_for_testing(
Now() + interval);
}
FakePrefs prefs_;
system::InputWatcherStub input_watcher_;
system::DisplayWatcherStub display_watcher_;
DBusSenderStub dbus_sender_;
TestInputControllerDelegate delegate_;
InputController controller_;
};
TEST_F(InputControllerTest, LidEvents) {
EXPECT_EQ(kNoActions, delegate_.GetActions());
// Initialization shouldn't generate a synthetic event.
prefs_.SetInt64(kUseLidPref, 1);
Init();
EXPECT_EQ(kNoActions, delegate_.GetActions());
EXPECT_EQ(0, dbus_sender_.num_sent_signals());
dbus_sender_.ClearSentSignals();
AdvanceTime(base::TimeDelta::FromSeconds(1));
input_watcher_.set_lid_state(LID_CLOSED);
input_watcher_.NotifyObserversAboutLidState();
EXPECT_EQ(kLidClosed, delegate_.GetActions());
EXPECT_EQ(InputEvent_Type_LID_CLOSED, GetInputEventSignalType());
EXPECT_EQ(Now().ToInternalValue(), GetInputEventSignalTimestamp());
dbus_sender_.ClearSentSignals();
AdvanceTime(base::TimeDelta::FromSeconds(5));
input_watcher_.set_lid_state(LID_OPEN);
input_watcher_.NotifyObserversAboutLidState();
EXPECT_EQ(kLidOpened, delegate_.GetActions());
EXPECT_EQ(InputEvent_Type_LID_OPEN, GetInputEventSignalType());
EXPECT_EQ(Now().ToInternalValue(), GetInputEventSignalTimestamp());
dbus_sender_.ClearSentSignals();
}
TEST_F(InputControllerTest, PowerButtonEvents) {
prefs_.SetInt64(kExternalDisplayOnlyPref, 1);
std::vector<system::DisplayInfo> displays(1, system::DisplayInfo());
display_watcher_.set_displays(displays);
Init();
input_watcher_.NotifyObserversAboutPowerButtonEvent(BUTTON_DOWN);
EXPECT_EQ(kPowerButtonDown, delegate_.GetActions());
EXPECT_EQ(InputEvent_Type_POWER_BUTTON_DOWN, GetInputEventSignalType());
EXPECT_EQ(Now().ToInternalValue(), GetInputEventSignalTimestamp());
dbus_sender_.ClearSentSignals();
AdvanceTime(base::TimeDelta::FromMilliseconds(100));
input_watcher_.NotifyObserversAboutPowerButtonEvent(BUTTON_UP);
EXPECT_EQ(kPowerButtonUp, delegate_.GetActions());
EXPECT_EQ(InputEvent_Type_POWER_BUTTON_UP, GetInputEventSignalType());
EXPECT_EQ(Now().ToInternalValue(), GetInputEventSignalTimestamp());
dbus_sender_.ClearSentSignals();
// With no displays connected, the system should shut down immediately.
displays.clear();
display_watcher_.set_displays(displays);
input_watcher_.NotifyObserversAboutPowerButtonEvent(BUTTON_DOWN);
EXPECT_EQ(kShutDown, delegate_.GetActions());
EXPECT_EQ(0, dbus_sender_.num_sent_signals());
}
TEST_F(InputControllerTest, DeferInactivityTimeoutWhileVT2IsActive) {
prefs_.SetInt64(kCheckActiveVTPref, 1);
Init();
input_watcher_.set_active_vt(1);
EXPECT_TRUE(controller_.TriggerCheckActiveVTTimeoutForTesting());
EXPECT_EQ(kNoActions, delegate_.GetActions());
input_watcher_.set_active_vt(2);
EXPECT_TRUE(controller_.TriggerCheckActiveVTTimeoutForTesting());
EXPECT_EQ(kDeferInactivity, delegate_.GetActions());
input_watcher_.set_active_vt(3);
EXPECT_TRUE(controller_.TriggerCheckActiveVTTimeoutForTesting());
EXPECT_EQ(kNoActions, delegate_.GetActions());
}
TEST_F(InputControllerTest, HonorCheckActiveVTPref) {
// The timer shouldn't be started if the check-active-VT pref is unset.
Init();
input_watcher_.set_active_vt(2);
EXPECT_FALSE(controller_.TriggerCheckActiveVTTimeoutForTesting());
EXPECT_EQ(kNoActions, delegate_.GetActions());
}
TEST_F(InputControllerTest, AcknowledgePowerButtonPresses) {
Init();
const base::TimeDelta kShortDelay = base::TimeDelta::FromMilliseconds(100);
const base::TimeDelta kTimeout = base::TimeDelta::FromMilliseconds(
InputController::kPowerButtonAcknowledgmentTimeoutMs);
// Press the power button, acknowledge the event nearly immediately, and check
// that no further actions are performed and that the timeout is stopped.
input_watcher_.NotifyObserversAboutPowerButtonEvent(BUTTON_DOWN);
EXPECT_EQ(kPowerButtonDown, delegate_.GetActions());
AdvanceTime(kShortDelay);
controller_.HandlePowerButtonAcknowledgment(
base::TimeTicks::FromInternalValue(GetInputEventSignalTimestamp()));
EXPECT_EQ(GetAcknowledgmentDelayAction(kShortDelay), delegate_.GetActions());
ASSERT_FALSE(controller_.TriggerPowerButtonAcknowledgmentTimeoutForTesting());
input_watcher_.NotifyObserversAboutPowerButtonEvent(BUTTON_UP);
EXPECT_EQ(kPowerButtonUp, delegate_.GetActions());
// Check that releasing the power button before it's been acknowledged also
// stops the timeout.
AdvanceTime(base::TimeDelta::FromSeconds(1));
input_watcher_.NotifyObserversAboutPowerButtonEvent(BUTTON_DOWN);
EXPECT_EQ(kPowerButtonDown, delegate_.GetActions());
input_watcher_.NotifyObserversAboutPowerButtonEvent(BUTTON_UP);
EXPECT_EQ(kPowerButtonUp, delegate_.GetActions());
ASSERT_FALSE(controller_.TriggerPowerButtonAcknowledgmentTimeoutForTesting());
dbus_sender_.ClearSentSignals();
// Let the timeout fire and check that the delegate is notified.
AdvanceTime(base::TimeDelta::FromSeconds(1));
input_watcher_.NotifyObserversAboutPowerButtonEvent(BUTTON_DOWN);
EXPECT_EQ(kPowerButtonDown, delegate_.GetActions());
ASSERT_TRUE(controller_.TriggerPowerButtonAcknowledgmentTimeoutForTesting());
EXPECT_EQ(JoinActions(GetAcknowledgmentDelayAction(kTimeout).c_str(),
kMissingPowerButtonAcknowledgment, NULL),
delegate_.GetActions());
ASSERT_FALSE(controller_.TriggerPowerButtonAcknowledgmentTimeoutForTesting());
input_watcher_.NotifyObserversAboutPowerButtonEvent(BUTTON_UP);
EXPECT_EQ(kPowerButtonUp, delegate_.GetActions());
// Send an acknowledgment with a stale timestamp and check that it doesn't
// stop the timeout.
AdvanceTime(base::TimeDelta::FromSeconds(1));
dbus_sender_.ClearSentSignals();
input_watcher_.NotifyObserversAboutPowerButtonEvent(BUTTON_DOWN);
EXPECT_EQ(kPowerButtonDown, delegate_.GetActions());
controller_.HandlePowerButtonAcknowledgment(
base::TimeTicks::FromInternalValue(GetInputEventSignalTimestamp() - 100));
EXPECT_EQ(kNoActions, delegate_.GetActions());
ASSERT_TRUE(controller_.TriggerPowerButtonAcknowledgmentTimeoutForTesting());
EXPECT_EQ(JoinActions(GetAcknowledgmentDelayAction(kTimeout).c_str(),
kMissingPowerButtonAcknowledgment, NULL),
delegate_.GetActions());
ASSERT_FALSE(controller_.TriggerPowerButtonAcknowledgmentTimeoutForTesting());
input_watcher_.NotifyObserversAboutPowerButtonEvent(BUTTON_UP);
EXPECT_EQ(kPowerButtonUp, delegate_.GetActions());
}
TEST_F(InputControllerTest, OnHoverStateChangedTest) {
Init();
input_watcher_.NotifyObserversAboutHoverState(true);
EXPECT_EQ(kHoverOn, delegate_.GetActions());
input_watcher_.NotifyObserversAboutHoverState(false);
EXPECT_EQ(kHoverOff, delegate_.GetActions());
}
} // namespace policy
} // namespace power_manager