Commit 0d1c005f authored by Maksim Ivanov's avatar Maksim Ivanov Committed by Commit Bot

Test of sign-in screen extensions auto-update

Add a browser test for the scenario when the sign-in screen extension
is first installed at some version, and then the server starts
announcing a newer version, so that Chrome eventually fetches, installs
and runs it.

Bug: 1012892
Change-Id: Iad3537236d56d7c86e02478bd2ff46fea6c0d112
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1992420
Commit-Queue: Maksim Ivanov <emaxx@chromium.org>
Reviewed-by: default avatarOleg Davydov <burunduk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#730201}
parent 01d66472
......@@ -7,10 +7,14 @@
#include "base/bind.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/version.h"
#include "chrome/browser/chromeos/policy/signin_profile_extensions_policy_test_base.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_context.h"
......@@ -24,8 +28,13 @@
#include "extensions/browser/notification_types.h"
#include "extensions/browser/test_extension_registry_observer.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/features/feature_channel.h"
#include "net/http/http_status_code.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace content {
class StoragePartition;
......@@ -41,8 +50,10 @@ namespace {
// * The manual testing app which is whitelisted for running in the sign-in
// profile:
const char kWhitelistedAppId[] = "bjaiihebfngildkcjkjckolinodhliff";
const char kWhitelistedAppUpdateManifestPath[] =
"/extensions/signin_screen_manual_test_app/update_manifest.xml";
const char kWhitelistedAppUpdateManifestPathFormat[] =
"/extensions/signin_screen_manual_test_app/crx/%s/update_manifest.xml";
const char kWhitelistedAppLatestVersion[] = "4.0";
const char kWhitelistedAppOlderVersion[] = "3.0";
// * A trivial test app which is NOT whitelisted for running in the sign-in
// profile:
const char kNotWhitelistedAppId[] = "mockapnacjbcdncmpkjngjalkhphojek";
......@@ -59,6 +70,13 @@ const char kNotWhitelistedExtensionId[] = "mockepjebcnmhmhcahfddgfcdgkdifnc";
const char kNotWhitelistedExtensionUpdateManifestPath[] =
"/extensions/trivial_extension/update_manifest.xml";
// Returns the update manifest path for the whitelisted testing app with the
// given version.
std::string GetWhitelistedAppUpdateManifestPath(const std::string& version) {
return base::StringPrintf(kWhitelistedAppUpdateManifestPathFormat,
version.c_str());
}
// Observer that allows waiting for an installation failure of a specific
// extension/app.
// TODO(emaxx): Extract this into a more generic helper class for using in other
......@@ -127,6 +145,50 @@ class ExtensionBackgroundPageReadyObserver final {
DISALLOW_COPY_AND_ASSIGN(ExtensionBackgroundPageReadyObserver);
};
// Observer that allows waiting until the specified version of the given
// extension/app gets installed.
class ExtensionInstallObserver final
: public extensions::ExtensionRegistryObserver {
public:
ExtensionInstallObserver(Profile* profile,
const std::string& extension_id,
const base::Version& awaited_version)
: profile_(profile),
extension_id_(extension_id),
awaited_version_(awaited_version) {
extensions::ExtensionRegistry::Get(profile)->AddObserver(this);
}
ExtensionInstallObserver(const ExtensionInstallObserver&) = delete;
ExtensionInstallObserver& operator=(const ExtensionInstallObserver&) = delete;
~ExtensionInstallObserver() override {
extensions::ExtensionRegistry::Get(profile_)->RemoveObserver(this);
}
// Should be called no more than once.
void Wait() {
// Note that the expected event could have already been observed before this
// point, in which case the run loop will exit immediately.
run_loop_.Run();
}
void OnExtensionInstalled(content::BrowserContext* browser_context,
const extensions::Extension* extension,
bool is_update) override {
if (extension->id() == extension_id_ &&
extension->version() == awaited_version_) {
run_loop_.Quit();
}
}
private:
Profile* const profile_;
const std::string extension_id_;
const base::Version awaited_version_;
base::RunLoop run_loop_;
};
// Class for testing sign-in profile apps/extensions that are installed via the
// device policy under different browser channels.
class SigninProfileExtensionsPolicyPerChannelTest
......@@ -160,8 +222,9 @@ IN_PROC_BROWSER_TEST_P(SigninProfileExtensionsPolicyPerChannelTest,
extensions::TestExtensionRegistryObserver registry_observer(
extensions::ExtensionRegistry::Get(profile), kWhitelistedAppId);
AddExtensionForForceInstallation(kWhitelistedAppId,
kWhitelistedAppUpdateManifestPath);
AddExtensionForForceInstallation(
kWhitelistedAppId,
GetWhitelistedAppUpdateManifestPath(kWhitelistedAppLatestVersion));
registry_observer.WaitForExtensionLoaded();
const extensions::Extension* extension =
......@@ -299,8 +362,9 @@ IN_PROC_BROWSER_TEST_F(SigninProfileExtensionsPolicyTest, MultipleApps) {
extensions::TestExtensionRegistryObserver registry_observer2(
extensions::ExtensionRegistry::Get(profile), kNotWhitelistedAppId);
AddExtensionForForceInstallation(kWhitelistedAppId,
kWhitelistedAppUpdateManifestPath);
AddExtensionForForceInstallation(
kWhitelistedAppId,
GetWhitelistedAppUpdateManifestPath(kWhitelistedAppLatestVersion));
AddExtensionForForceInstallation(kNotWhitelistedAppId,
kNotWhitelistedUpdateManifestPath);
......@@ -318,8 +382,9 @@ IN_PROC_BROWSER_TEST_F(SigninProfileExtensionsPolicyTest,
ExtensionBackgroundPageReadyObserver page_observer_for_extension(
kWhitelistedExtensionId);
AddExtensionForForceInstallation(kWhitelistedAppId,
kWhitelistedAppUpdateManifestPath);
AddExtensionForForceInstallation(
kWhitelistedAppId,
GetWhitelistedAppUpdateManifestPath(kWhitelistedAppLatestVersion));
AddExtensionForForceInstallation(kWhitelistedExtensionId,
kWhitelistedExtensionUpdateManifestPath);
......@@ -355,8 +420,9 @@ class SigninProfileExtensionsPolicyOfflineLaunchTest
extensions::ExtensionRegistry::Get(GetInitialProfile()),
kWhitelistedAppId);
AddExtensionForForceInstallation(kWhitelistedAppId,
kWhitelistedAppUpdateManifestPath);
AddExtensionForForceInstallation(
kWhitelistedAppId,
GetWhitelistedAppUpdateManifestPath(kWhitelistedAppLatestVersion));
// In the non-PRE test, this simulates inability to make network requests
// for fetching the extension update manifest and CRX files. In the PRE test
......@@ -395,4 +461,135 @@ IN_PROC_BROWSER_TEST_F(SigninProfileExtensionsPolicyOfflineLaunchTest, Test) {
WaitForTestExtensionLoaded();
}
// Class for testing the auto update of the sign-in profile extensions.
class SigninProfileExtensionsAutoUpdatePolicyTest
: public SigninProfileExtensionsPolicyTest {
public:
SigninProfileExtensionsAutoUpdatePolicyTest() {
embedded_test_server()->RegisterRequestHandler(base::Bind(
&SigninProfileExtensionsAutoUpdatePolicyTest::HandleTestServerRequest,
base::Unretained(this)));
}
void SetUpOnMainThread() override {
SigninProfileExtensionsPolicyTest::SetUpOnMainThread();
test_extension_registry_observer_ =
std::make_unique<extensions::TestExtensionRegistryObserver>(
extensions::ExtensionRegistry::Get(GetInitialProfile()),
kWhitelistedAppId);
test_extension_latest_version_install_observer_ =
std::make_unique<ExtensionInstallObserver>(
GetInitialProfile(), kWhitelistedAppId,
base::Version(kWhitelistedAppLatestVersion));
AddExtensionForForceInstallation(kWhitelistedAppId,
kRedirectingUpdateManifestPath);
}
void TearDownOnMainThread() override {
test_extension_latest_version_install_observer_.reset();
test_extension_registry_observer_.reset();
SigninProfileExtensionsPolicyTest::TearDownOnMainThread();
}
// Enables serving the test extension's update manifest at the specified
// version.
void StartServingTestExtension(const std::string& extension_version) {
served_extension_version_ = extension_version;
}
void WaitForTestExtensionLoaded() {
test_extension_registry_observer_->WaitForExtensionLoaded();
}
void WaitForTestExtensionLatestVersionInstalled() {
test_extension_latest_version_install_observer_->Wait();
}
base::Version GetTestExtensionVersion() {
const extensions::Extension* const extension =
extensions::ExtensionRegistry::Get(GetInitialProfile())
->enabled_extensions()
.GetByID(kWhitelistedAppId);
if (!extension)
return base::Version();
return extension->version();
}
private:
// Path on the embedded test server that redirects to the update manifest of
// the test extension for the version that is currently served.
const std::string kRedirectingUpdateManifestPath =
"/redirecting-update-manifest-path.xml";
// Handler for the embedded test server. Provides special behavior for the
// test extension's update manifest URL in accordance to
// |served_extension_version_|.
std::unique_ptr<net::test_server::HttpResponse> HandleTestServerRequest(
const net::test_server::HttpRequest& request) {
if (request.GetURL().path() != kRedirectingUpdateManifestPath)
return nullptr;
if (served_extension_version_.empty()) {
// No extension is served now, so return an error.
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_code(net::HTTP_INTERNAL_SERVER_ERROR);
return response;
}
// Redirect to the XML file for the corresponding version.
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_code(net::HTTP_TEMPORARY_REDIRECT);
response->AddCustomHeader("Location",
embedded_test_server()
->GetURL(GetWhitelistedAppUpdateManifestPath(
served_extension_version_))
.spec());
return response;
}
// Specifies which version of the test extension needs to be served. An empty
// string means that no version is served.
std::string served_extension_version_;
std::unique_ptr<extensions::TestExtensionRegistryObserver>
test_extension_registry_observer_;
std::unique_ptr<ExtensionInstallObserver>
test_extension_latest_version_install_observer_;
};
// This is the first preparation step for the actual test. Here the old version
// of the whitelisted app is served, and it gets installed into the sign-in
// profile.
IN_PROC_BROWSER_TEST_F(SigninProfileExtensionsAutoUpdatePolicyTest,
PRE_PRE_Test) {
StartServingTestExtension(kWhitelistedAppOlderVersion);
WaitForTestExtensionLoaded();
EXPECT_EQ(GetTestExtensionVersion(),
base::Version(kWhitelistedAppOlderVersion));
}
// This is the second preparation step for the actual test. Here the new version
// of the app is served, and it gets fetched and installed.
IN_PROC_BROWSER_TEST_F(SigninProfileExtensionsAutoUpdatePolicyTest, PRE_Test) {
// Let the extensions system to load the previously fetched version before
// starting to serve the newer version, to avoid hitting flaky DCHECKs in the
// extensions system internals (see https://crbug.com/810799).
WaitForTestExtensionLoaded();
EXPECT_EQ(GetTestExtensionVersion(),
base::Version(kWhitelistedAppOlderVersion));
// Start serving the newer version and verify that it gets installed.
StartServingTestExtension(kWhitelistedAppLatestVersion);
WaitForTestExtensionLatestVersionInstalled();
}
// This is the actual test. Here we verify that the new version of the app, as
// fetched in the PRE_Test, gets launched even in the "offline" mode (since
// we're not serving any version of the extension in this part of the test).
IN_PROC_BROWSER_TEST_F(SigninProfileExtensionsAutoUpdatePolicyTest, Test) {
WaitForTestExtensionLoaded();
EXPECT_EQ(GetTestExtensionVersion(),
base::Version(kWhitelistedAppLatestVersion));
}
} // namespace policy
The app_signed_by_webstore.crx package must be a one signed by WebStore, in
order for the app to have the expected ID which is whitelisted in Chrome -
"bjaiihebfngildkcjkjckolinodhliff".
This app is primarily intended to be used for the manual testing of the apps on
the Chrome OS sign-in screen.
the Chrome OS sign-in screen. The sources are located under the ./app/
directory.
The ./crx/**/app_signed_by_webstore.crx packages must be files signed by
WebStore, in order for the app to have the expected ID which is whitelisted in
Chrome - "bjaiihebfngildkcjkjckolinodhliff".
Browser tests in Chrome currently require these packages for two different
versions.
In case a new version of the test app has to be published on WebStore, please
reach out to the managed-devices@ mailing list.
......@@ -7,7 +7,7 @@
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
<app appid='bjaiihebfngildkcjkjckolinodhliff'>
<updatecheck
codebase='http://mock.http/extensions/signin_screen_manual_test_app/app_signed_by_webstore.crx'
codebase='http://mock.http/extensions/signin_screen_manual_test_app/crx/3.0/app_signed_by_webstore.crx'
version='3.0' />
</app>
</gupdate>
<?xml version='1.0' encoding='UTF-8'?>
<!--
This update manifest points to the ./app_signed_by_webstore.crx file.
"mock.http" is a placeholder that gets substituted with the test server
address in runtime.
-->
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
<app appid='bjaiihebfngildkcjkjckolinodhliff'>
<updatecheck
codebase='http://mock.http/extensions/signin_screen_manual_test_app/crx/4.0/app_signed_by_webstore.crx'
version='4.0' />
</app>
</gupdate>
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