-
Notifications
You must be signed in to change notification settings - Fork 6.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
crosier: Port login.Chrome and login.ChromeGaia
- Add `ChromeOSIntegrationLoginMixIn` that provides login support - It provides 3 login modes: - kStubLogin: Login like browser tests, Good for tests only need to verify chrome states. - kTestLogin: Login using oobe test api, same as tast's default login. Good for test that needs to simulate real login but do not need Gaia ideneity; - kGaiaLogin: Login using Gaia like production chrome. - Implement login integration tests that exercise kTestLogin (login.Chrome) and kGaiaLogin (login.ChromeGaia). Bug: b:301445988 Change-Id: If4d3bd0450058ff46b60539b4a12738e7bb88279 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4953973 Reviewed-by: James Cook <jamescook@chromium.org> Commit-Queue: Xiyuan Xia <xiyuan@chromium.org> Reviewed-by: Brett Wilson <brettw@chromium.org> Reviewed-by: Scott Violet <sky@chromium.org> Cr-Commit-Position: refs/heads/main@{#1212847}
- Loading branch information
Xiyuan Xia
authored and
Chromium LUCI CQ
committed
Oct 20, 2023
1 parent
5b8c4b0
commit 4ca35db
Showing
9 changed files
with
403 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Copyright 2023 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include <string> | ||
#include <vector> | ||
|
||
#include "build/branding_buildflags.h" | ||
#include "chrome/browser/ash/login/test/session_manager_state_waiter.h" | ||
#include "chrome/test/base/chromeos/crosier/chromeos_integration_login_mixin.h" | ||
#include "chrome/test/base/chromeos/crosier/interactive_ash_test.h" | ||
|
||
class LoginIntegrationTest : public InteractiveAshTest { | ||
public: | ||
LoginIntegrationTest() { | ||
set_exit_when_last_browser_closes(false); | ||
|
||
login_mixin().SetMode(ChromeOSIntegrationLoginMixin::Mode::kTestLogin); | ||
} | ||
|
||
LoginIntegrationTest(const LoginIntegrationTest&) = delete; | ||
LoginIntegrationTest& operator=(const LoginIntegrationTest&) = delete; | ||
|
||
~LoginIntegrationTest() override = default; | ||
}; | ||
|
||
IN_PROC_BROWSER_TEST_F(LoginIntegrationTest, TestLogin) { | ||
login_mixin().Login(); | ||
|
||
// Waits for the primary user session to start. | ||
ash::test::WaitForPrimaryUserSessionStart(); | ||
} | ||
|
||
#if BUILDFLAG(GOOGLE_CHROME_BRANDING) | ||
// Gaia login is only supported for branded build. | ||
class GaiaLoginIntegrationTest : public InteractiveAshTest { | ||
public: | ||
GaiaLoginIntegrationTest() { | ||
set_exit_when_last_browser_closes(false); | ||
|
||
// Allows network access for production Gaia. | ||
SetAllowNetworkAccessToHostResolutions(); | ||
|
||
login_mixin().SetMode(ChromeOSIntegrationLoginMixin::Mode::kGaiaLogin); | ||
} | ||
|
||
GaiaLoginIntegrationTest(const GaiaLoginIntegrationTest&) = delete; | ||
GaiaLoginIntegrationTest& operator=(const GaiaLoginIntegrationTest&) = delete; | ||
|
||
~GaiaLoginIntegrationTest() override = default; | ||
}; | ||
|
||
IN_PROC_BROWSER_TEST_F(GaiaLoginIntegrationTest, GaiaLogin) { | ||
login_mixin().Login(); | ||
|
||
// Waits for the primary user session to start. | ||
ash::test::WaitForPrimaryUserSessionStart(); | ||
} | ||
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
230 changes: 230 additions & 0 deletions
230
chrome/test/base/chromeos/crosier/chromeos_integration_login_mixin.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
// Copyright 2023 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "chrome/test/base/chromeos/crosier/chromeos_integration_login_mixin.h" | ||
|
||
#include "ash/constants/ash_switches.h" | ||
#include "base/threading/thread_restrictions.h" | ||
#include "build/branding_buildflags.h" | ||
#include "chrome/browser/ash/dbus/ash_dbus_helper.h" | ||
#include "chrome/browser/ash/login/test/js_checker.h" | ||
#include "chrome/browser/ash/login/test/oobe_screen_waiter.h" | ||
#include "chrome/browser/ash/login/test/oobe_screens_utils.h" | ||
#include "chrome/browser/ash/login/wizard_controller.h" | ||
#include "chrome/browser/lifetime/application_lifetime.h" | ||
#include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h" | ||
#include "chrome/browser/ui/webui/signin/signin_utils.h" | ||
#include "chrome/test/base/chromeos/crosier/test_accounts.h" | ||
#include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h" | ||
|
||
namespace { | ||
|
||
class FakeSessionManagerClientBrowserHelper | ||
: public ash::DBusHelperObserverForTest { | ||
public: | ||
FakeSessionManagerClientBrowserHelper() { | ||
ash::DBusHelperObserverForTest::Set(this); | ||
} | ||
FakeSessionManagerClientBrowserHelper( | ||
const FakeSessionManagerClientBrowserHelper&) = delete; | ||
FakeSessionManagerClientBrowserHelper& operator=( | ||
const FakeSessionManagerClientBrowserHelper&) = delete; | ||
~FakeSessionManagerClientBrowserHelper() override { | ||
ash::DBusHelperObserverForTest::Set(nullptr); | ||
} | ||
|
||
// ash::DBusHelperObserverForTest: | ||
void PostInitializeDBus() override { | ||
// Create FakeSessionManageClient after real SessionManagerClient is created | ||
// and before it is referenced. | ||
scoped_fake_session_manager_client_.emplace(); | ||
ash::FakeSessionManagerClient::Get()->set_stop_session_callback( | ||
base::BindOnce(&chrome::ExitIgnoreUnloadHandlers)); | ||
} | ||
|
||
void PreShutdownDBus() override { | ||
// Release FakeSessionManagerClient shutting down dbus clients. | ||
scoped_fake_session_manager_client_.reset(); | ||
} | ||
|
||
private: | ||
// Optionally, use FakeSessionManagerClient if a test only needs the stub | ||
// user session. | ||
absl::optional<ash::ScopedFakeSessionManagerClient> | ||
scoped_fake_session_manager_client_; | ||
}; | ||
|
||
#if BUILDFLAG(GOOGLE_CHROME_BRANDING) | ||
content::RenderFrameHost* GetGaiaHost() { | ||
constexpr char kGaiaFrameParentId[] = "signin-frame"; | ||
return signin::GetAuthFrame( | ||
ash::LoginDisplayHost::default_host()->GetOobeWebContents(), | ||
kGaiaFrameParentId); | ||
} | ||
|
||
ash::test::JSChecker GaiaFrameJS() { | ||
return ash::test::JSChecker(GetGaiaHost()); | ||
} | ||
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) | ||
|
||
} // namespace | ||
|
||
ChromeOSIntegrationLoginMixin::ChromeOSIntegrationLoginMixin( | ||
InProcessBrowserTestMixinHost* host) | ||
: InProcessBrowserTestMixin(host) {} | ||
|
||
ChromeOSIntegrationLoginMixin::~ChromeOSIntegrationLoginMixin() { | ||
sudo_helper_client_.EnsureSessionManagerStopped(); | ||
} | ||
|
||
void ChromeOSIntegrationLoginMixin::SetMode(Mode mode) { | ||
CHECK(!setup_called_); | ||
mode_ = mode; | ||
} | ||
|
||
void ChromeOSIntegrationLoginMixin::Login() { | ||
switch (mode_) { | ||
case Mode::kStubLogin: { | ||
// Nothing to do since stub user should be signed in on start. | ||
break; | ||
} | ||
case Mode::kTestLogin: { | ||
DoTestLogin(); | ||
break; | ||
} | ||
case Mode::kGaiaLogin: { | ||
DoGaiaLogin(); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
void ChromeOSIntegrationLoginMixin::SetUp() { | ||
setup_called_ = true; | ||
|
||
switch (mode_) { | ||
case Mode::kStubLogin: { | ||
fake_session_manager_client_helper_ = | ||
std::make_unique<FakeSessionManagerClientBrowserHelper>(); | ||
break; | ||
} | ||
case Mode::kTestLogin: { | ||
[[fallthrough]]; | ||
} | ||
case Mode::kGaiaLogin: { | ||
PrepareForNewUserLogin(); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
void ChromeOSIntegrationLoginMixin::SetUpCommandLine( | ||
base::CommandLine* command_line) { | ||
command_line->AppendSwitch( | ||
ash::switches::kDisableHIDDetectionOnOOBEForTesting); | ||
|
||
if (mode_ != Mode::kGaiaLogin) { | ||
command_line->AppendSwitch(ash::switches::kDisableGaiaServices); | ||
} | ||
|
||
if (ShouldStartLoginScreen()) { | ||
command_line->AppendSwitch(ash::switches::kLoginManager); | ||
command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests); | ||
command_line->AppendSwitch( | ||
ash::switches::kDisableOOBEChromeVoxHintTimerForTesting); | ||
command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "user"); | ||
} | ||
|
||
if (mode_ == Mode::kTestLogin) { | ||
// Needed to use Oobe test api to login. | ||
command_line->AppendSwitch(ash::switches::kEnableOobeTestAPI); | ||
} | ||
} | ||
|
||
bool ChromeOSIntegrationLoginMixin::ShouldStartLoginScreen() const { | ||
return mode_ == Mode::kTestLogin || mode_ == Mode::kGaiaLogin; | ||
} | ||
|
||
void ChromeOSIntegrationLoginMixin::PrepareForNewUserLogin() { | ||
CHECK(sudo_helper_client_.RunCommand("./reset_dut.py").return_code == 0); | ||
|
||
// Starts session_manager daemon and use `chrome::ExitIgnoreUnloadHandlers` as | ||
// the session_manager stopped callback. The callback would be invoked | ||
// when session_manager daemon terminates (getting a StopSession dbus call, | ||
// or crashed). | ||
// | ||
// In normal case, SessionManagerClient::StopSession would be called when a | ||
// test case finishes. The daemon would terminate and `test_sudo_helper.py` | ||
// script sends a message back to `TestSudoHelperClient`, which triggers the | ||
// callback. | ||
// | ||
// For the crashed case, the callback would be invoked before the test case | ||
// finishes and cause it to fail. | ||
auto result = sudo_helper_client_.StartSessionManager( | ||
base::BindOnce(&chrome::ExitIgnoreUnloadHandlers)); | ||
CHECK_EQ(result.return_code, 0); | ||
} | ||
|
||
void ChromeOSIntegrationLoginMixin::DoTestLogin() { | ||
ash::test::WaitForOobeJSReady(); | ||
|
||
// Any gmail account and password works for test login. | ||
constexpr char kTestUser[] = "testuser@gmail.com"; | ||
constexpr char kTestPassword[] = "testpass"; | ||
constexpr char kTestGaiaId[] = "12345"; | ||
|
||
ash::test::OobeJS().Evaluate( | ||
base::StringPrintf("Oobe.loginForTesting(\"%s\", \"%s\",\"%s\")", | ||
kTestUser, kTestPassword, kTestGaiaId)); | ||
|
||
// Skip post login steps, such as ToS etc. | ||
ash::WizardController::default_controller()->SkipPostLoginScreensForTesting(); | ||
} | ||
|
||
void ChromeOSIntegrationLoginMixin::DoGaiaLogin() { | ||
#if BUILDFLAG(GOOGLE_CHROME_BRANDING) | ||
// Skip to login screen. | ||
ash::WizardController::default_controller()->SkipToLoginForTesting(); | ||
ash::OobeScreenWaiter(ash::GaiaView::kScreenId).Wait(); | ||
|
||
// Wait for Gaia page to load. | ||
while (!GetGaiaHost()) { | ||
base::RunLoop run_loop; | ||
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( | ||
FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(500)); | ||
run_loop.Run(); | ||
} | ||
|
||
std::string email; | ||
std::string password; | ||
|
||
{ | ||
// Allows reading account pool json file. | ||
base::ScopedAllowBlockingForTesting allow_blocking; | ||
|
||
crosier::GetGaiaTestAccount(email, password); | ||
CHECK(!email.empty() && !password.empty()); | ||
} | ||
|
||
GaiaFrameJS() | ||
.CreateWaiter("!!document.querySelector('#identifierId')") | ||
->Wait(); | ||
GaiaFrameJS().Evaluate(base::StrCat( | ||
{"document.querySelector('#identifierId').value=\"", email, "\""})); | ||
ash::test::OobeJS().Evaluate("Oobe.clickGaiaPrimaryButtonForTesting()"); | ||
|
||
GaiaFrameJS() | ||
.CreateWaiter("!!document.querySelector('input[type=password]')") | ||
->Wait(); | ||
GaiaFrameJS().Evaluate( | ||
base::StrCat({"document.querySelector('input[type=password]').value=\"", | ||
password, "\""})); | ||
ash::test::OobeJS().Evaluate("Oobe.clickGaiaPrimaryButtonForTesting()"); | ||
|
||
// Skip post login steps, such as ToS etc. | ||
ash::WizardController::default_controller()->SkipPostLoginScreensForTesting(); | ||
#else | ||
CHECK(false) << "Gaia login is only supported in branded build."; | ||
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) | ||
} |
77 changes: 77 additions & 0 deletions
77
chrome/test/base/chromeos/crosier/chromeos_integration_login_mixin.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// Copyright 2023 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#ifndef CHROME_TEST_BASE_CHROMEOS_CROSIER_CHROMEOS_INTEGRATION_LOGIN_MIXIN_H_ | ||
#define CHROME_TEST_BASE_CHROMEOS_CROSIER_CHROMEOS_INTEGRATION_LOGIN_MIXIN_H_ | ||
|
||
#include <memory> | ||
#include <string> | ||
|
||
#include "chrome/test/base/chromeos/crosier/helper/test_sudo_helper_client.h" | ||
#include "chrome/test/base/mixin_based_in_process_browser_test.h" | ||
|
||
namespace { | ||
class FakeSessionManagerClientBrowserHelper; | ||
} | ||
|
||
// Provides login supports to ChromeOS integration tests. | ||
class ChromeOSIntegrationLoginMixin : public InProcessBrowserTestMixin { | ||
public: | ||
enum class Mode { | ||
// Mode starts from the chrome restart code path. It skips the login screen | ||
// and starts a stub user session automatically. It does not run session | ||
// manager daemon so it will not notify system daemons about user sign-in | ||
// state. It is intended for tests that do not care about login and only | ||
// test states in chrome. | ||
kStubLogin, | ||
|
||
// Mode starts from the login screen and uses test api to login. It starts | ||
// session_manager daemon and does cryptohome mount like production chrome. | ||
// Tests that do not depend on gaia identity should use this mode. | ||
kTestLogin, | ||
|
||
// Mode is the same as `kTestLogin` except that it uses the production gaia | ||
// server to authenticate. Tests that need gaia identity should use this | ||
// mode. | ||
kGaiaLogin, | ||
}; | ||
|
||
explicit ChromeOSIntegrationLoginMixin(InProcessBrowserTestMixinHost* host); | ||
ChromeOSIntegrationLoginMixin(const ChromeOSIntegrationLoginMixin&) = delete; | ||
ChromeOSIntegrationLoginMixin& operator=( | ||
const ChromeOSIntegrationLoginMixin&) = delete; | ||
~ChromeOSIntegrationLoginMixin() override; | ||
|
||
// Set the login mode. Must be called before SetUp. | ||
void SetMode(Mode mode); | ||
|
||
// Signs in a test account. For kTestLogin, it is a fake testuser@gmail.com. | ||
// For kGaiaLogin, the account is randomly picked from `gaiaPoolDefault`. | ||
void Login(); | ||
|
||
// InProcessBrowserTestMixin: | ||
void SetUp() override; | ||
void SetUpCommandLine(base::CommandLine* command_line) override; | ||
|
||
private: | ||
bool ShouldStartLoginScreen() const; | ||
void PrepareForNewUserLogin(); | ||
|
||
// Performs login for kTestLogin mode by using Oobe test api. | ||
void DoTestLogin(); | ||
|
||
// Performs login for kGaiaLogin mode by authenticating through production | ||
// gaia server. | ||
void DoGaiaLogin(); | ||
|
||
bool setup_called_ = false; | ||
|
||
Mode mode_ = Mode::kStubLogin; | ||
|
||
std::unique_ptr<FakeSessionManagerClientBrowserHelper> | ||
fake_session_manager_client_helper_; | ||
TestSudoHelperClient sudo_helper_client_; | ||
}; | ||
|
||
#endif // CHROME_TEST_BASE_CHROMEOS_CROSIER_CHROMEOS_INTEGRATION_LOGIN_MIXIN_H_ |
Oops, something went wrong.