Commit ec9eead7 authored by Jacob Dufault's avatar Jacob Dufault Committed by Commit Bot

cros: Add views-lock test mojo api and add an interactive ui test for it

Validate that we can show the lock screen, enter a bad password, and then enter
a good password.

Bug: 899777
Change-Id: Ie3c8ff629524374362db5fe51f648defdf5331dc
Reviewed-on: https://chromium-review.googlesource.com/c/1323701
Commit-Queue: Jacob Dufault <jdufault@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#608990}
parent 7a895ac8
...@@ -2119,6 +2119,8 @@ static_library("test_support_common") { ...@@ -2119,6 +2119,8 @@ static_library("test_support_common") {
"lock_screen_action/lock_screen_action_background_view_test_api.h", "lock_screen_action/lock_screen_action_background_view_test_api.h",
"lock_screen_action/test_lock_screen_action_background_controller.cc", "lock_screen_action/test_lock_screen_action_background_controller.cc",
"lock_screen_action/test_lock_screen_action_background_controller.h", "lock_screen_action/test_lock_screen_action_background_controller.h",
"login/login_screen_test_api.cc",
"login/login_screen_test_api.h",
"metrics/task_switch_time_tracker_test_api.cc", "metrics/task_switch_time_tracker_test_api.cc",
"metrics/task_switch_time_tracker_test_api.h", "metrics/task_switch_time_tracker_test_api.h",
"metrics/time_to_first_present_recorder_test_api.cc", "metrics/time_to_first_present_recorder_test_api.cc",
......
// Copyright 2018 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/login/login_screen_test_api.h"
#include <memory>
#include <utility>
#include "ash/login/ui/lock_contents_view.h"
#include "ash/login/ui/lock_screen.h"
#include "ash/login/ui/lock_window.h"
#include "ash/login/ui/login_auth_user_view.h"
#include "ash/login/ui/login_big_user_view.h"
#include "ash/login/ui/login_password_view.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "ui/views/controls/textfield/textfield.h"
namespace ash {
// static
void LoginScreenTestApi::BindRequest(mojom::LoginScreenTestApiRequest request) {
mojo::MakeStrongBinding(std::make_unique<LoginScreenTestApi>(),
std::move(request));
}
LoginScreenTestApi::LoginScreenTestApi() = default;
LoginScreenTestApi::~LoginScreenTestApi() = default;
void LoginScreenTestApi::IsLockShown(IsLockShownCallback callback) {
std::move(callback).Run(
LockScreen::HasInstance() && LockScreen::Get()->is_shown() &&
LockScreen::Get()->screen_type() == LockScreen::ScreenType::kLock);
}
void LoginScreenTestApi::SubmitPassword(const AccountId& account_id,
const std::string& password,
SubmitPasswordCallback callback) {
// It'd be better to generate keyevents dynamically and dispatch them instead
// of reaching into the views structure, but at the time of writing I could
// not find a good way to do this. If you know of a way feel free to change
// this code.
LockScreen::TestApi lock_screen_test(LockScreen::Get());
LockContentsView::TestApi lock_contents_test(
lock_screen_test.contents_view());
LoginAuthUserView::TestApi auth_test(
lock_contents_test.primary_big_view()->auth_user());
LoginPasswordView::TestApi password_test(auth_test.password_view());
// For the time being, only the primary user is supported. To support multiple
// users this API needs to search all user views for the associated user and
// potentially activate that user so it is showing its password field.
CHECK_EQ(account_id,
auth_test.user_view()->current_user()->basic_user_info->account_id);
password_test.SubmitPassword(password);
std::move(callback).Run();
}
} // namespace ash
// Copyright 2018 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.
#ifndef ASH_LOGIN_LOGIN_SCREEN_TEST_API_H_
#define ASH_LOGIN_LOGIN_SCREEN_TEST_API_H_
#include "ash/public/interfaces/login_screen_test_api.mojom.h"
#include "base/macros.h"
#include "components/account_id/account_id.h"
namespace ash {
// Allows tests to access private state of the login/lock screens.
class LoginScreenTestApi : public mojom::LoginScreenTestApi {
public:
// Creates and binds an instance from a remote request (e.g. from chrome).
static void BindRequest(mojom::LoginScreenTestApiRequest request);
LoginScreenTestApi();
~LoginScreenTestApi() override;
// mojom::LoginScreen:
void IsLockShown(IsLockShownCallback callback) override;
void SubmitPassword(const AccountId& account_id,
const std::string& password,
SubmitPasswordCallback callback) override;
private:
DISALLOW_COPY_AND_ASSIGN(LoginScreenTestApi);
};
} // namespace ash
#endif // ASH_LOGIN_LOGIN_SCREEN_TEST_API_H_
...@@ -115,6 +115,8 @@ class AuthErrorLearnMoreButton : public views::Button, ...@@ -115,6 +115,8 @@ class AuthErrorLearnMoreButton : public views::Button,
label->SetFontList(base_font_list.Derive(0, gfx::Font::FontStyle::NORMAL, label->SetFontList(base_font_list.Derive(0, gfx::Font::FontStyle::NORMAL,
gfx::Font::Weight::NORMAL)); gfx::Font::Weight::NORMAL));
AddChildView(label); AddChildView(label);
SetAccessibleName(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE));
} }
void ButtonPressed(Button* sender, const ui::Event& event) override { void ButtonPressed(Button* sender, const ui::Event& event) override {
......
...@@ -304,6 +304,12 @@ LoginPasswordView::TestApi::TestApi(LoginPasswordView* view) : view_(view) {} ...@@ -304,6 +304,12 @@ LoginPasswordView::TestApi::TestApi(LoginPasswordView* view) : view_(view) {}
LoginPasswordView::TestApi::~TestApi() = default; LoginPasswordView::TestApi::~TestApi() = default;
void LoginPasswordView::TestApi::SubmitPassword(const std::string& password) {
view_->textfield_->SetText(base::ASCIIToUTF16(password));
view_->UpdateUiState();
view_->SubmitPassword();
}
views::Textfield* LoginPasswordView::TestApi::textfield() const { views::Textfield* LoginPasswordView::TestApi::textfield() const {
return view_->textfield_; return view_->textfield_;
} }
......
...@@ -48,6 +48,8 @@ class ASH_EXPORT LoginPasswordView : public views::View, ...@@ -48,6 +48,8 @@ class ASH_EXPORT LoginPasswordView : public views::View,
explicit TestApi(LoginPasswordView* view); explicit TestApi(LoginPasswordView* view);
~TestApi(); ~TestApi();
void SubmitPassword(const std::string& password);
views::Textfield* textfield() const; views::Textfield* textfield() const;
views::View* submit_button() const; views::View* submit_button() const;
views::View* easy_unlock_icon() const; views::View* easy_unlock_icon() const;
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
], ],
// Test-only interfaces. // Test-only interfaces.
"test": [ "test": [
"ash.mojom.LoginScreenTestApi",
"ash.mojom.ShelfTestApi", "ash.mojom.ShelfTestApi",
"ash.mojom.ShellTestApi", "ash.mojom.ShellTestApi",
"ash.mojom.StatusAreaWidgetTestApi", "ash.mojom.StatusAreaWidgetTestApi",
......
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
#include <utility> #include <utility>
#include "ash/login/login_screen_test_api.h"
#include "ash/metrics/time_to_first_present_recorder_test_api.h" #include "ash/metrics/time_to_first_present_recorder_test_api.h"
#include "ash/public/interfaces/login_screen_test_api.mojom.h"
#include "ash/public/interfaces/shelf_test_api.mojom.h" #include "ash/public/interfaces/shelf_test_api.mojom.h"
#include "ash/public/interfaces/shell_test_api.mojom.h" #include "ash/public/interfaces/shell_test_api.mojom.h"
#include "ash/public/interfaces/status_area_widget_test_api.mojom.h" #include "ash/public/interfaces/status_area_widget_test_api.mojom.h"
...@@ -26,6 +28,11 @@ namespace { ...@@ -26,6 +28,11 @@ namespace {
// These functions aren't strictly necessary, but exist to make threading and // These functions aren't strictly necessary, but exist to make threading and
// arguments clearer. // arguments clearer.
void BindLoginScreenTestApiOnMainThread(
mojom::LoginScreenTestApiRequest request) {
LoginScreenTestApi::BindRequest(std::move(request));
}
void BindShelfTestApiOnMainThread(mojom::ShelfTestApiRequest request) { void BindShelfTestApiOnMainThread(mojom::ShelfTestApiRequest request) {
ShelfTestApi::BindRequest(std::move(request)); ShelfTestApi::BindRequest(std::move(request));
} }
...@@ -54,6 +61,8 @@ void BindTimeToFirstPresentRecorderTestApiOnMainThread( ...@@ -54,6 +61,8 @@ void BindTimeToFirstPresentRecorderTestApiOnMainThread(
void RegisterInterfaces( void RegisterInterfaces(
service_manager::BinderRegistry* registry, service_manager::BinderRegistry* registry,
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner) { scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner) {
registry->AddInterface(base::Bind(&BindLoginScreenTestApiOnMainThread),
main_thread_task_runner);
registry->AddInterface(base::Bind(&BindShelfTestApiOnMainThread), registry->AddInterface(base::Bind(&BindShelfTestApiOnMainThread),
main_thread_task_runner); main_thread_task_runner);
registry->AddInterface(base::Bind(&BindShellTestApiOnMainThread), registry->AddInterface(base::Bind(&BindShellTestApiOnMainThread),
......
...@@ -98,6 +98,7 @@ mojom("test_interfaces") { ...@@ -98,6 +98,7 @@ mojom("test_interfaces") {
testonly = true testonly = true
disable_variants = true disable_variants = true
sources = [ sources = [
"login_screen_test_api.mojom",
"shelf_test_api.mojom", "shelf_test_api.mojom",
"shell_test_api.mojom", "shell_test_api.mojom",
"status_area_widget_test_api.mojom", "status_area_widget_test_api.mojom",
...@@ -105,6 +106,7 @@ mojom("test_interfaces") { ...@@ -105,6 +106,7 @@ mojom("test_interfaces") {
"time_to_first_present_recorder_test_api.mojom", "time_to_first_present_recorder_test_api.mojom",
] ]
deps = [ deps = [
"//components/account_id/interfaces",
"//mojo/public/mojom/base", "//mojo/public/mojom/base",
"//ui/gfx/geometry/mojo", "//ui/gfx/geometry/mojo",
] ]
......
// Copyright 2018 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.
module ash.mojom;
import "components/account_id/interfaces/account_id.mojom";
// Provides a high-level test API for controlling the login/lock screen.
interface LoginScreenTestApi {
// Returns true if the lock screen is currently being shown.
IsLockShown() => (bool is_shown);
// Submit |password| for |account_id|.
SubmitPassword(signin.mojom.AccountId account_id, string password) => ();
};
...@@ -147,6 +147,42 @@ class WebUiScreenLockerTest : public ScreenLockerTest { ...@@ -147,6 +147,42 @@ class WebUiScreenLockerTest : public ScreenLockerTest {
DISALLOW_COPY_AND_ASSIGN(WebUiScreenLockerTest); DISALLOW_COPY_AND_ASSIGN(WebUiScreenLockerTest);
}; };
IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestBasic) {
// Show lock screen and wait until it is shown.
std::unique_ptr<ScreenLockerTester> tester = ScreenLockerTester::Create();
content::WindowedNotificationObserver lock_state_observer(
chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED,
content::NotificationService::AllSources());
ScreenLocker::Show();
if (!tester->IsLocked())
lock_state_observer.Wait();
EXPECT_EQ(session_manager::SessionState::LOCKED,
session_manager::SessionManager::Get()->session_state());
// Inject fake authentication credentials.
UserContext user_context(user_manager::UserType::USER_TYPE_REGULAR,
user_manager::StubAccountId());
user_context.SetKey(Key("pass"));
tester->InjectStubUserContext(user_context);
EXPECT_TRUE(tester->IsLocked());
// Submit a bad password.
tester->EnterPassword(user_manager::StubAccountId(), "fail");
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(tester->IsLocked());
// Submit the correct password. Successful authentication clears the lock
// screen and tells the SessionManager to announce this over DBus.
tester->EnterPassword(user_manager::StubAccountId(), "pass");
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(tester->IsLocked());
EXPECT_EQ(
1, fake_session_manager_client_->notify_lock_screen_shown_call_count());
EXPECT_EQ(session_manager::SessionState::ACTIVE,
session_manager::SessionManager::Get()->session_state());
EXPECT_TRUE(VerifyLockScreenDismissed());
}
IN_PROC_BROWSER_TEST_F(WebUiScreenLockerTest, TestBasic) { IN_PROC_BROWSER_TEST_F(WebUiScreenLockerTest, TestBasic) {
// WebUiScreenLockerTest fails with Mash because of unexpected window // WebUiScreenLockerTest fails with Mash because of unexpected window
// structure. Fortunately we will deprecate the WebUI-based screen locker // structure. Fortunately we will deprecate the WebUI-based screen locker
...@@ -168,10 +204,10 @@ IN_PROC_BROWSER_TEST_F(WebUiScreenLockerTest, TestBasic) { ...@@ -168,10 +204,10 @@ IN_PROC_BROWSER_TEST_F(WebUiScreenLockerTest, TestBasic) {
user_context.SetKey(Key("pass")); user_context.SetKey(Key("pass"));
tester->InjectStubUserContext(user_context); tester->InjectStubUserContext(user_context);
EXPECT_TRUE(tester->IsLocked()); EXPECT_TRUE(tester->IsLocked());
tester->EnterPassword("fail"); tester->EnterPassword(user_manager::StubAccountId(), "fail");
content::RunAllPendingInMessageLoop(); content::RunAllPendingInMessageLoop();
EXPECT_TRUE(tester->IsLocked()); EXPECT_TRUE(tester->IsLocked());
tester->EnterPassword("pass"); tester->EnterPassword(user_manager::StubAccountId(), "pass");
content::RunAllPendingInMessageLoop(); content::RunAllPendingInMessageLoop();
// Successful authentication clears the lock screen and tells the // Successful authentication clears the lock screen and tells the
// SessionManager to announce this over DBus. // SessionManager to announce this over DBus.
...@@ -236,7 +272,7 @@ IN_PROC_BROWSER_TEST_F(WebUiScreenLockerTest, MAYBE_TestFullscreenExit) { ...@@ -236,7 +272,7 @@ IN_PROC_BROWSER_TEST_F(WebUiScreenLockerTest, MAYBE_TestFullscreenExit) {
user_manager::StubAccountId()); user_manager::StubAccountId());
user_context.SetKey(Key("pass")); user_context.SetKey(Key("pass"));
tester->InjectStubUserContext(user_context); tester->InjectStubUserContext(user_context);
tester->EnterPassword("pass"); tester->EnterPassword(user_manager::StubAccountId(), "pass");
content::RunAllPendingInMessageLoop(); content::RunAllPendingInMessageLoop();
EXPECT_FALSE(tester->IsLocked()); EXPECT_FALSE(tester->IsLocked());
{ {
...@@ -277,7 +313,7 @@ IN_PROC_BROWSER_TEST_F(WebUiScreenLockerTest, MAYBE_TestFullscreenExit) { ...@@ -277,7 +313,7 @@ IN_PROC_BROWSER_TEST_F(WebUiScreenLockerTest, MAYBE_TestFullscreenExit) {
EXPECT_TRUE(tester->IsLocked()); EXPECT_TRUE(tester->IsLocked());
} }
tester->EnterPassword("pass"); tester->EnterPassword(user_manager::StubAccountId(), "pass");
content::RunAllPendingInMessageLoop(); content::RunAllPendingInMessageLoop();
EXPECT_FALSE(tester->IsLocked()); EXPECT_FALSE(tester->IsLocked());
...@@ -288,15 +324,6 @@ IN_PROC_BROWSER_TEST_F(WebUiScreenLockerTest, MAYBE_TestFullscreenExit) { ...@@ -288,15 +324,6 @@ IN_PROC_BROWSER_TEST_F(WebUiScreenLockerTest, MAYBE_TestFullscreenExit) {
fake_session_manager_client_->notify_lock_screen_dismissed_call_count()); fake_session_manager_client_->notify_lock_screen_dismissed_call_count());
} }
void SimulateKeyPress(views::Widget* widget, ui::KeyboardCode key_code) {
ui_controls::SendKeyPress(widget->GetNativeWindow(), key_code, false, false,
false, false);
}
void UnlockKeyPress(views::Widget* widget) {
SimulateKeyPress(widget, ui::VKEY_SPACE);
}
IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestShowTwice) { IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestShowTwice) {
std::unique_ptr<ScreenLockerTester> tester = ScreenLockerTester::Create(); std::unique_ptr<ScreenLockerTester> tester = ScreenLockerTester::Create();
LockScreen(tester.get()); LockScreen(tester.get());
......
...@@ -6,6 +6,10 @@ ...@@ -6,6 +6,10 @@
#include <string> #include <string>
#include "ash/public/cpp/ash_switches.h"
#include "ash/public/interfaces/constants.mojom.h"
#include "ash/public/interfaces/login_screen_test_api.mojom.h"
#include "base/command_line.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
...@@ -18,12 +22,19 @@ ...@@ -18,12 +22,19 @@
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui.h"
#include "content/public/common/service_manager_connection.h"
#include "content/public/test/test_utils.h" #include "content/public/test/test_utils.h"
#include "services/service_manager/public/cpp/connector.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace chromeos { namespace chromeos {
namespace { namespace {
bool IsScreenLockerLocked() {
return ScreenLocker::default_screen_locker() &&
ScreenLocker::default_screen_locker()->locked();
}
// This class is used to observe state of the global ScreenLocker instance, // This class is used to observe state of the global ScreenLocker instance,
// which can go away as a result of a successful authentication. As such, // which can go away as a result of a successful authentication. As such,
// it needs to directly reference the global ScreenLocker. // it needs to directly reference the global ScreenLocker.
...@@ -71,7 +82,9 @@ class WebUIScreenLockerTester : public ScreenLockerTester { ...@@ -71,7 +82,9 @@ class WebUIScreenLockerTester : public ScreenLockerTester {
~WebUIScreenLockerTester() override = default; ~WebUIScreenLockerTester() override = default;
// ScreenLockerTester: // ScreenLockerTester:
void EnterPassword(const std::string& password) override { bool IsLocked() override { return IsScreenLockerLocked(); }
void EnterPassword(const AccountId& account_id,
const std::string& password) override {
bool result; bool result;
SetPassword(password); SetPassword(password);
...@@ -135,16 +148,49 @@ class WebUIScreenLockerTester : public ScreenLockerTester { ...@@ -135,16 +148,49 @@ class WebUIScreenLockerTester : public ScreenLockerTester {
DISALLOW_COPY_AND_ASSIGN(WebUIScreenLockerTester); DISALLOW_COPY_AND_ASSIGN(WebUIScreenLockerTester);
}; };
class MojoScreenLockerTester : public ScreenLockerTester {
public:
MojoScreenLockerTester() {
content::ServiceManagerConnection::GetForProcess()
->GetConnector()
->BindInterface(ash::mojom::kServiceName, &test_api_);
}
~MojoScreenLockerTester() override = default;
// ScreenLockerTester:
bool IsLocked() override {
// Check from ash's perspective.
ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
bool is_ui_shown;
login_screen.IsLockShown(&is_ui_shown);
return IsScreenLockerLocked() && is_ui_shown;
}
void EnterPassword(const AccountId& account_id,
const std::string& password) override {
ash::mojom::LoginScreenTestApiAsyncWaiter login_screen(test_api_.get());
login_screen.SubmitPassword(account_id, password);
}
private:
ash::mojom::LoginScreenTestApiPtr test_api_;
DISALLOW_COPY_AND_ASSIGN(MojoScreenLockerTester);
};
} // namespace } // namespace
// static // static
std::unique_ptr<ScreenLockerTester> ScreenLockerTester::Create() { std::unique_ptr<ScreenLockerTester> ScreenLockerTester::Create() {
return std::make_unique<WebUIScreenLockerTester>(); if (base::CommandLine::ForCurrentProcess()->HasSwitch(
ash::switches::kShowWebUiLock)) {
return std::make_unique<WebUIScreenLockerTester>();
}
return std::make_unique<MojoScreenLockerTester>();
} }
ScreenLockerTester::ScreenLockerTester() {} ScreenLockerTester::ScreenLockerTester() = default;
ScreenLockerTester::~ScreenLockerTester() {} ScreenLockerTester::~ScreenLockerTester() = default;
void ScreenLockerTester::InjectStubUserContext( void ScreenLockerTester::InjectStubUserContext(
const UserContext& user_context) { const UserContext& user_context) {
...@@ -154,9 +200,4 @@ void ScreenLockerTester::InjectStubUserContext( ...@@ -154,9 +200,4 @@ void ScreenLockerTester::InjectStubUserContext(
base::MakeRefCounted<FakeExtendedAuthenticator>(locker, user_context)); base::MakeRefCounted<FakeExtendedAuthenticator>(locker, user_context));
} }
bool ScreenLockerTester::IsLocked() {
return ScreenLocker::default_screen_locker() &&
ScreenLocker::default_screen_locker()->locked();
}
} // namespace chromeos } // namespace chromeos
...@@ -8,12 +8,14 @@ ...@@ -8,12 +8,14 @@
#include <memory> #include <memory>
#include <string> #include <string>
class AccountId;
namespace chromeos { namespace chromeos {
class UserContext; class UserContext;
// ScreenLockerTester provides access to the private state/function // ScreenLockerTester provides a high-level API to test the lock screen. This
// of ScreenLocker class. Used to implement unit tests. // API is meant to be representation independent.
class ScreenLockerTester { class ScreenLockerTester {
public: public:
// Create a new tester. // Create a new tester.
...@@ -23,13 +25,14 @@ class ScreenLockerTester { ...@@ -23,13 +25,14 @@ class ScreenLockerTester {
virtual ~ScreenLockerTester(); virtual ~ScreenLockerTester();
// Returns true if the screen is locked. // Returns true if the screen is locked.
virtual bool IsLocked(); virtual bool IsLocked() = 0;
// Injects StubAuthenticator that uses the credentials in |user_context|. // Injects StubAuthenticator that uses the credentials in |user_context|.
virtual void InjectStubUserContext(const UserContext& user_context); virtual void InjectStubUserContext(const UserContext& user_context);
// Enters and submits the given password. // Enters and submits the given password.
virtual void EnterPassword(const std::string& password) = 0; virtual void EnterPassword(const AccountId& account_id,
const std::string& password) = 0;
}; };
} // namespace chromeos } // namespace chromeos
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment