blob: c69575e062028d4b908be4f1ab3593083c109318 [file] [log] [blame]
// Copyright 2014 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 "ash/system/session/logout_confirmation_controller.h"
#include "ash/session/test_session_controller_client.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/ref_counted.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/tick_clock.h"
#include "components/session_manager/session_manager_types.h"
#include "components/user_manager/user_type.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/window.h"
#include "ui/views/widget/widget.h"
namespace ash {
class LogoutConfirmationControllerTest : public testing::Test {
protected:
LogoutConfirmationControllerTest();
~LogoutConfirmationControllerTest() override;
void LogOut();
bool log_out_called_;
scoped_refptr<base::TestMockTimeTaskRunner> runner_;
base::ThreadTaskRunnerHandle runner_handle_;
LogoutConfirmationController controller_;
private:
DISALLOW_COPY_AND_ASSIGN(LogoutConfirmationControllerTest);
};
LogoutConfirmationControllerTest::LogoutConfirmationControllerTest()
: log_out_called_(false),
runner_(new base::TestMockTimeTaskRunner),
runner_handle_(runner_) {
controller_.SetClockForTesting(runner_->GetMockTickClock());
controller_.SetLogoutClosureForTesting(base::Bind(
&LogoutConfirmationControllerTest::LogOut, base::Unretained(this)));
}
LogoutConfirmationControllerTest::~LogoutConfirmationControllerTest() = default;
void LogoutConfirmationControllerTest::LogOut() {
log_out_called_ = true;
}
// Verifies that the user is logged out immediately if logout confirmation with
// a zero-length countdown is requested.
TEST_F(LogoutConfirmationControllerTest, ZeroDuration) {
controller_.ConfirmLogout(runner_->NowTicks());
EXPECT_FALSE(log_out_called_);
runner_->FastForwardBy(base::TimeDelta());
EXPECT_TRUE(log_out_called_);
}
// Verifies that the user is logged out when the countdown expires.
TEST_F(LogoutConfirmationControllerTest, DurationExpired) {
controller_.ConfirmLogout(runner_->NowTicks() +
base::TimeDelta::FromSeconds(10));
EXPECT_FALSE(log_out_called_);
runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
EXPECT_FALSE(log_out_called_);
runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
EXPECT_TRUE(log_out_called_);
}
// Verifies that when a second request to confirm logout is made and the second
// request's countdown ends before the original request's, the user is logged
// out when the new countdown expires.
TEST_F(LogoutConfirmationControllerTest, DurationShortened) {
controller_.ConfirmLogout(runner_->NowTicks() +
base::TimeDelta::FromSeconds(30));
EXPECT_FALSE(log_out_called_);
runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
EXPECT_FALSE(log_out_called_);
controller_.ConfirmLogout(runner_->NowTicks() +
base::TimeDelta::FromSeconds(10));
runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
EXPECT_FALSE(log_out_called_);
runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
EXPECT_TRUE(log_out_called_);
}
// Verifies that when a second request to confirm logout is made and the second
// request's countdown ends after the original request's, the user is logged
// out when the original countdown expires.
TEST_F(LogoutConfirmationControllerTest, DurationExtended) {
controller_.ConfirmLogout(runner_->NowTicks() +
base::TimeDelta::FromSeconds(10));
EXPECT_FALSE(log_out_called_);
runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
EXPECT_FALSE(log_out_called_);
controller_.ConfirmLogout(runner_->NowTicks() +
base::TimeDelta::FromSeconds(10));
runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
EXPECT_TRUE(log_out_called_);
}
// Verifies that when the screen is locked while the countdown is running, the
// user is not logged out, even when the original countdown expires.
TEST_F(LogoutConfirmationControllerTest, Lock) {
controller_.ConfirmLogout(runner_->NowTicks() +
base::TimeDelta::FromSeconds(10));
EXPECT_FALSE(log_out_called_);
controller_.OnLockStateChanged(true);
runner_->FastForwardUntilNoTasksRemain();
EXPECT_FALSE(log_out_called_);
}
// Verifies that when the user confirms the logout request, the user is logged
// out immediately.
TEST_F(LogoutConfirmationControllerTest, UserAccepted) {
controller_.ConfirmLogout(runner_->NowTicks() +
base::TimeDelta::FromSeconds(10));
EXPECT_FALSE(log_out_called_);
controller_.OnLogoutConfirmed();
EXPECT_TRUE(log_out_called_);
}
// Verifies that when the user denies the logout request, the user is not logged
// out, even when the original countdown expires.
TEST_F(LogoutConfirmationControllerTest, UserDenied) {
controller_.ConfirmLogout(runner_->NowTicks() +
base::TimeDelta::FromSeconds(10));
EXPECT_FALSE(log_out_called_);
controller_.OnDialogClosed();
runner_->FastForwardUntilNoTasksRemain();
EXPECT_FALSE(log_out_called_);
}
// Verifies that after the user has denied a logout request, a subsequent logout
// request is handled correctly and the user is logged out when the countdown
// expires.
TEST_F(LogoutConfirmationControllerTest, DurationExpiredAfterDeniedRequest) {
controller_.ConfirmLogout(runner_->NowTicks() +
base::TimeDelta::FromSeconds(10));
EXPECT_FALSE(log_out_called_);
controller_.OnDialogClosed();
runner_->FastForwardUntilNoTasksRemain();
EXPECT_FALSE(log_out_called_);
controller_.ConfirmLogout(runner_->NowTicks() +
base::TimeDelta::FromSeconds(10));
EXPECT_FALSE(log_out_called_);
runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
EXPECT_FALSE(log_out_called_);
runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
EXPECT_TRUE(log_out_called_);
}
class LastWindowClosedTest : public NoSessionAshTestBase {
public:
LastWindowClosedTest() = default;
~LastWindowClosedTest() override = default;
// Simulate a public account signing in.
void StartPublicAccountSession() {
TestSessionControllerClient* session = GetSessionControllerClient();
session->Reset();
session->AddUserSession("user1@test.com",
user_manager::USER_TYPE_PUBLIC_ACCOUNT);
session->SetSessionState(session_manager::SessionState::ACTIVE);
}
private:
DISALLOW_COPY_AND_ASSIGN(LastWindowClosedTest);
};
TEST_F(LastWindowClosedTest, RegularSession) {
// Dialog is not visible at startup.
LogoutConfirmationController* controller =
Shell::Get()->logout_confirmation_controller();
EXPECT_FALSE(controller->dialog_for_testing());
// Dialog is not visible after login.
CreateUserSessions(1);
EXPECT_FALSE(controller->dialog_for_testing());
// Creating and closing a window does not show the dialog because this is not
// a public account session.
std::unique_ptr<aura::Window> window = CreateToplevelTestWindow();
EXPECT_FALSE(controller->dialog_for_testing());
window.reset();
EXPECT_FALSE(controller->dialog_for_testing());
}
TEST_F(LastWindowClosedTest, PublicSession) {
LogoutConfirmationController* controller =
Shell::Get()->logout_confirmation_controller();
// Dialog is not visible after public account login.
StartPublicAccountSession();
EXPECT_FALSE(controller->dialog_for_testing());
// Opening windows does not show the dialog.
std::unique_ptr<views::Widget> widget1 = CreateTestWidget();
std::unique_ptr<views::Widget> widget2 = CreateTestWidget();
EXPECT_FALSE(controller->dialog_for_testing());
// Closing the last window shows the dialog.
widget1.reset();
EXPECT_FALSE(controller->dialog_for_testing());
widget2.reset();
EXPECT_TRUE(controller->dialog_for_testing());
}
TEST_F(LastWindowClosedTest, AlwaysOnTop) {
LogoutConfirmationController* controller =
Shell::Get()->logout_confirmation_controller();
StartPublicAccountSession();
// The new widget starts in the default window container.
std::unique_ptr<views::Widget> widget = CreateTestWidget();
// Moving the widget to the always-on-top container does not trigger the
// dialog because the window didn't close.
widget->SetAlwaysOnTop(true);
EXPECT_FALSE(controller->dialog_for_testing());
// Closing the window triggers the dialog.
widget.reset();
EXPECT_TRUE(controller->dialog_for_testing());
}
TEST_F(LastWindowClosedTest, MultipleContainers) {
LogoutConfirmationController* controller =
Shell::Get()->logout_confirmation_controller();
StartPublicAccountSession();
// Create two windows in different containers.
std::unique_ptr<views::Widget> normal_widget = CreateTestWidget();
std::unique_ptr<views::Widget> always_on_top_widget = CreateTestWidget();
always_on_top_widget->SetAlwaysOnTop(true);
// Closing the last window shows the dialog.
always_on_top_widget.reset();
EXPECT_FALSE(controller->dialog_for_testing());
normal_widget.reset();
EXPECT_TRUE(controller->dialog_for_testing());
}
TEST_F(LastWindowClosedTest, MultipleDisplays) {
LogoutConfirmationController* controller =
Shell::Get()->logout_confirmation_controller();
StartPublicAccountSession();
// Create two displays, each with a window.
UpdateDisplay("1024x768,800x600");
std::unique_ptr<aura::Window> window1 =
CreateChildWindow(Shell::GetAllRootWindows()[0]->GetChildById(
kShellWindowId_DefaultContainer));
std::unique_ptr<aura::Window> window2 =
CreateChildWindow(Shell::GetAllRootWindows()[1]->GetChildById(
kShellWindowId_DefaultContainer));
// Closing the last window shows the dialog.
window1.reset();
EXPECT_FALSE(controller->dialog_for_testing());
window2.reset();
EXPECT_TRUE(controller->dialog_for_testing());
}
} // namespace ash