0

Fix Handoff from Omaha to O4 fails

The updater scope was not taking the legacy command line format into
account, now fixed by determining the scope using the legacy-compatible
command line.

Added integration tests.

Bug: 1379746
Change-Id: I42386daf3276156ab34e6ac17094b0b6439a8898
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4111693
Reviewed-by: Sorin Jianu <sorin@chromium.org>
Commit-Queue: Sorin Jianu <sorin@chromium.org>
Commit-Queue: S Ganesh <ganesh@chromium.org>
Auto-Submit: S Ganesh <ganesh@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1084071}
This commit is contained in:
S. Ganesh
2022-12-15 23:57:38 +00:00
committed by Chromium LUCI CQ
parent d1d8d7bee4
commit 1b1e771923
10 changed files with 158 additions and 52 deletions

@@ -46,6 +46,11 @@ class IntegrationTestCommands
const std::string& install_data_index, const std::string& install_data_index,
const base::Version& from_version, const base::Version& from_version,
const base::Version& to_version) const = 0; const base::Version& to_version) const = 0;
virtual void ExpectInstallSequence(ScopedServer* test_server,
const std::string& app_id,
const std::string& install_data_index,
const base::Version& from_version,
const base::Version& to_version) const = 0;
virtual void ExpectVersionActive(const std::string& version) const = 0; virtual void ExpectVersionActive(const std::string& version) const = 0;
virtual void ExpectVersionNotActive(const std::string& version) const = 0; virtual void ExpectVersionNotActive(const std::string& version) const = 0;
virtual void Uninstall() const = 0; virtual void Uninstall() const = 0;
@@ -90,6 +95,7 @@ class IntegrationTestCommands
virtual void RunUninstallCmdLine() const = 0; virtual void RunUninstallCmdLine() const = 0;
virtual void SetUpTestService() const = 0; virtual void SetUpTestService() const = 0;
virtual void TearDownTestService() const = 0; virtual void TearDownTestService() const = 0;
virtual void RunHandoff(const std::string& app_id) const = 0;
#endif // BUILDFLAG(IS_WIN) #endif // BUILDFLAG(IS_WIN)
virtual void StressUpdateService() const = 0; virtual void StressUpdateService() const = 0;
virtual void CallServiceUpdate(const std::string& app_id, virtual void CallServiceUpdate(const std::string& app_id,

@@ -102,6 +102,16 @@ class IntegrationTestCommandsSystem : public IntegrationTestCommands {
to_version); to_version);
} }
void ExpectInstallSequence(ScopedServer* test_server,
const std::string& app_id,
const std::string& install_data_index,
const base::Version& from_version,
const base::Version& to_version) const override {
updater::test::ExpectInstallSequence(updater_scope_, test_server, app_id,
install_data_index, from_version,
to_version);
}
void ExpectVersionActive(const std::string& version) const override { void ExpectVersionActive(const std::string& version) const override {
RunCommand("expect_version_active", {Param("version", version)}); RunCommand("expect_version_active", {Param("version", version)});
} }
@@ -251,6 +261,10 @@ class IntegrationTestCommandsSystem : public IntegrationTestCommands {
void TearDownTestService() const override { void TearDownTestService() const override {
updater::test::RunTestServiceCommand("teardown"); updater::test::RunTestServiceCommand("teardown");
} }
void RunHandoff(const std::string& app_id) const override {
RunCommand("run_handoff", {Param("app_id", app_id)});
}
#endif // BUILDFLAG(IS_WIN) #endif // BUILDFLAG(IS_WIN)
base::FilePath GetDifferentUserPath() const override { base::FilePath GetDifferentUserPath() const override {

@@ -83,6 +83,16 @@ class IntegrationTestCommandsUser : public IntegrationTestCommands {
to_version); to_version);
} }
void ExpectInstallSequence(ScopedServer* test_server,
const std::string& app_id,
const std::string& install_data_index,
const base::Version& from_version,
const base::Version& to_version) const override {
updater::test::ExpectInstallSequence(updater_scope_, test_server, app_id,
install_data_index, from_version,
to_version);
}
void ExpectVersionActive(const std::string& version) const override { void ExpectVersionActive(const std::string& version) const override {
updater::test::ExpectVersionActive(updater_scope_, version); updater::test::ExpectVersionActive(updater_scope_, version);
} }
@@ -218,6 +228,10 @@ class IntegrationTestCommandsUser : public IntegrationTestCommands {
void SetUpTestService() const override {} void SetUpTestService() const override {}
void TearDownTestService() const override {} void TearDownTestService() const override {}
void RunHandoff(const std::string& app_id) const override {
updater::test::RunHandoff(updater_scope_, app_id);
}
#endif // BUILDFLAG(IS_WIN) #endif // BUILDFLAG(IS_WIN)
base::FilePath GetDifferentUserPath() const override { base::FilePath GetDifferentUserPath() const override {

@@ -194,6 +194,10 @@ class IntegrationTest : public ::testing::Test {
} }
void RunUninstallCmdLine() { test_commands_->RunUninstallCmdLine(); } void RunUninstallCmdLine() { test_commands_->RunUninstallCmdLine(); }
void RunHandoff(const std::string& app_id) {
test_commands_->RunHandoff(app_id);
}
#endif // BUILDFLAG(IS_WIN) #endif // BUILDFLAG(IS_WIN)
void SetupFakeUpdaterHigherVersion() { void SetupFakeUpdaterHigherVersion() {
@@ -308,14 +312,13 @@ class IntegrationTest : public ::testing::Test {
test_commands_->ExpectSelfUpdateSequence(test_server); test_commands_->ExpectSelfUpdateSequence(test_server);
} }
void ExpectInstallEvent(ScopedServer* test_server, void ExpectInstallSequence(ScopedServer* test_server,
const std::string& app_id) { const std::string& app_id,
test_server->ExpectOnce( const std::string& install_data_index,
{base::BindRepeating( const base::Version& from_version,
RequestMatcherRegex, const base::Version& to_version) {
base::StrCat({R"(.*"appid":")", app_id, R"(","enabled":true,")", test_commands_->ExpectInstallSequence(
R"(event":\[{"eventresult":1,"eventtype":2,.*)"}))}, test_server, app_id, install_data_index, from_version, to_version);
"");
} }
void StressUpdateService() { test_commands_->StressUpdateService(); } void StressUpdateService() { test_commands_->StressUpdateService(); }
@@ -561,6 +564,21 @@ TEST_F(IntegrationTest, UpdateApp) {
} }
#if BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_WIN)
TEST_F(IntegrationTest, Handoff) {
ScopedServer test_server(test_commands_);
ASSERT_NO_FATAL_FAILURE(Install());
const std::string kAppId("test");
const base::Version v1("1");
ExpectInstallSequence(&test_server, kAppId, "", base::Version({0, 0, 0, 0}),
v1);
RunHandoff(kAppId);
EXPECT_TRUE(WaitForUpdaterExit());
ExpectAppVersion(kAppId, v1);
Uninstall();
}
TEST_F(IntegrationTest, ForceInstallApp) { TEST_F(IntegrationTest, ForceInstallApp) {
ScopedServer test_server(test_commands_); ScopedServer test_server(test_commands_);
ASSERT_NO_FATAL_FAILURE(Install()); ASSERT_NO_FATAL_FAILURE(Install());

@@ -273,6 +273,7 @@ void AppTestHelper::FirstTaskRun() {
{"expect_legacy_policy_status_succeeds", {"expect_legacy_policy_status_succeeds",
WithSystemScope(Wrap(&ExpectLegacyPolicyStatusSucceeds))}, WithSystemScope(Wrap(&ExpectLegacyPolicyStatusSucceeds))},
{"run_uninstall_cmd_line", WithSystemScope(Wrap(&RunUninstallCmdLine))}, {"run_uninstall_cmd_line", WithSystemScope(Wrap(&RunUninstallCmdLine))},
{"run_handoff", WithSwitch("app_id", WithSystemScope(Wrap(&RunHandoff)))},
#endif // BUILDFLAG(IS_WIN) #endif // BUILDFLAG(IS_WIN)
{"expect_version_active", {"expect_version_active",
WithSwitch("version", WithSystemScope(Wrap(&ExpectVersionActive)))}, WithSwitch("version", WithSystemScope(Wrap(&ExpectVersionActive)))},

@@ -194,6 +194,57 @@ void RunUpdaterWithSwitch(const base::Version& version,
EXPECT_EQ(exit_code, expected_exit_code); EXPECT_EQ(exit_code, expected_exit_code);
} }
void ExpectSequence(UpdaterScope scope,
ScopedServer* test_server,
const std::string& app_id,
const std::string& install_data_index,
int event_type,
const base::Version& from_version,
const base::Version& to_version) {
base::FilePath test_data_path;
ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_path));
base::FilePath crx_path = test_data_path.Append(FILE_PATH_LITERAL("updater"))
.AppendASCII(kDoNothingCRXName);
ASSERT_TRUE(base::PathExists(crx_path));
// First request: update check.
test_server->ExpectOnce(
{base::BindRepeating(
RequestMatcherRegex,
base::StringPrintf(R"(.*"appid":"%s".*)", app_id.c_str())),
base::BindRepeating(
RequestMatcherRegex,
base::StringPrintf(
R"(.*%s)",
!install_data_index.empty()
? base::StringPrintf(
R"("data":\[{"index":"%s","name":"install"}],.*)",
install_data_index.c_str())
.c_str()
: "")),
GetScopePredicate(scope)},
GetUpdateResponse(app_id, install_data_index,
test_server->base_url().spec(), to_version, crx_path,
kDoNothingCRXRun, {}));
// Second request: update download.
std::string crx_bytes;
base::ReadFileToString(crx_path, &crx_bytes);
test_server->ExpectOnce({base::BindRepeating(RequestMatcherRegex, "")},
crx_bytes);
// Third request: event ping.
test_server->ExpectOnce(
{base::BindRepeating(
RequestMatcherRegex,
base::StringPrintf(R"(.*"eventresult":1,"eventtype":%d,)"
R"("nextversion":"%s","previousversion":"%s".*)",
event_type, to_version.GetString().c_str(),
from_version.GetString().c_str())),
GetScopePredicate(scope)},
")]}'\n");
}
} // namespace } // namespace
void ExitTestMode(UpdaterScope scope) { void ExitTestMode(UpdaterScope scope) {
@@ -544,48 +595,18 @@ void ExpectUpdateSequence(UpdaterScope scope,
const std::string& install_data_index, const std::string& install_data_index,
const base::Version& from_version, const base::Version& from_version,
const base::Version& to_version) { const base::Version& to_version) {
base::FilePath test_data_path; ExpectSequence(scope, test_server, app_id, install_data_index, 3,
ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_path)); from_version, to_version);
base::FilePath crx_path = test_data_path.Append(FILE_PATH_LITERAL("updater")) }
.AppendASCII(kDoNothingCRXName);
ASSERT_TRUE(base::PathExists(crx_path));
// First request: update check. void ExpectInstallSequence(UpdaterScope scope,
test_server->ExpectOnce( ScopedServer* test_server,
{base::BindRepeating( const std::string& app_id,
RequestMatcherRegex, const std::string& install_data_index,
base::StringPrintf(R"(.*"appid":"%s".*)", app_id.c_str())), const base::Version& from_version,
base::BindRepeating( const base::Version& to_version) {
RequestMatcherRegex, ExpectSequence(scope, test_server, app_id, install_data_index, 2,
base::StringPrintf( from_version, to_version);
R"(.*%s)",
!install_data_index.empty()
? base::StringPrintf(
R"("data":\[{"index":"%s","name":"install"}],.*)",
install_data_index.c_str())
.c_str()
: "")),
GetScopePredicate(scope)},
GetUpdateResponse(app_id, install_data_index,
test_server->base_url().spec(), to_version, crx_path,
kDoNothingCRXRun, {}));
// Second request: update download.
std::string crx_bytes;
base::ReadFileToString(crx_path, &crx_bytes);
test_server->ExpectOnce({base::BindRepeating(RequestMatcherRegex, "")},
crx_bytes);
// Third request: event ping.
test_server->ExpectOnce(
{base::BindRepeating(
RequestMatcherRegex,
base::StringPrintf(R"(.*"eventresult":1,"eventtype":3,)"
R"("nextversion":"%s","previousversion":"%s".*)",
to_version.GetString().c_str(),
from_version.GetString().c_str())),
GetScopePredicate(scope)},
")]}'\n");
} }
// Runs multiple cycles of instantiating the update service, calling // Runs multiple cycles of instantiating the update service, calling

@@ -215,6 +215,7 @@ void InvokeTestServiceFunction(const std::string& function_name,
const base::Value::Dict& arguments); const base::Value::Dict& arguments);
void RunUninstallCmdLine(UpdaterScope scope); void RunUninstallCmdLine(UpdaterScope scope);
void RunHandoff(UpdaterScope scope, const std::string& app_id);
#endif // BUILDFLAG(IS_WIN) #endif // BUILDFLAG(IS_WIN)
// Returns the number of files in the directory, not including directories, // Returns the number of files in the directory, not including directories,
@@ -234,6 +235,13 @@ void ExpectUpdateSequence(UpdaterScope scope,
const base::Version& from_version, const base::Version& from_version,
const base::Version& to_version); const base::Version& to_version);
void ExpectInstallSequence(UpdaterScope scope,
ScopedServer* test_server,
const std::string& app_id,
const std::string& install_data_index,
const base::Version& from_version,
const base::Version& to_version);
void StressUpdateService(UpdaterScope scope); void StressUpdateService(UpdaterScope scope);
void CallServiceUpdate(UpdaterScope updater_scope, void CallServiceUpdate(UpdaterScope updater_scope,
@@ -259,7 +267,6 @@ void UninstallApp(UpdaterScope scope, const std::string& app_id);
void RunOfflineInstall(UpdaterScope scope, void RunOfflineInstall(UpdaterScope scope,
bool is_legacy_install, bool is_legacy_install,
bool is_silent_install); bool is_silent_install);
} // namespace updater::test } // namespace updater::test
#endif // CHROME_UPDATER_TEST_INTEGRATION_TESTS_IMPL_H_ #endif // CHROME_UPDATER_TEST_INTEGRATION_TESTS_IMPL_H_

@@ -1410,6 +1410,27 @@ void RunUninstallCmdLine(UpdaterScope scope) {
EXPECT_EQ(0, exit_code); EXPECT_EQ(0, exit_code);
} }
void RunHandoff(UpdaterScope scope, const std::string& app_id) {
const absl::optional<base::FilePath> installed_executable_path =
GetInstalledExecutablePath(scope);
ASSERT_TRUE(installed_executable_path);
ASSERT_TRUE(base::PathExists(*installed_executable_path));
base::ScopedAllowBaseSyncPrimitivesForTesting allow_wait_process;
const std::wstring command_line(base::StrCat(
{installed_executable_path->value(), L" /handoff \"appguid=",
base::ASCIIToWide(app_id), L"&needsadmin=",
IsSystemInstall(scope) ? L"Prefers" : L"False", L"\" /silent"}));
VLOG(0) << " RunHandoff: " << command_line;
const base::Process process = base::LaunchProcess(command_line, {});
ASSERT_TRUE(process.IsValid());
int exit_code = 0;
ASSERT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(),
&exit_code));
ASSERT_EQ(exit_code, 0);
}
void SetupFakeLegacyUpdaterData(UpdaterScope scope) { void SetupFakeLegacyUpdaterData(UpdaterScope scope) {
const HKEY root = UpdaterScopeToHKeyRoot(scope); const HKEY root = UpdaterScopeToHKeyRoot(scope);

@@ -8,11 +8,11 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/updater/constants.h" #include "chrome/updater/constants.h"
#include "chrome/updater/util/util.h"
#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/abseil-cpp/absl/types/optional.h"
#if BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_WIN)
#include "chrome/updater/tag.h" #include "chrome/updater/tag.h"
#include "chrome/updater/util/util.h"
#include "chrome/updater/util/win_util.h" #include "chrome/updater/util/win_util.h"
#endif #endif
@@ -73,7 +73,7 @@ UpdaterScope GetUpdaterScopeForCommandLine(
} }
UpdaterScope GetUpdaterScope() { UpdaterScope GetUpdaterScope() {
return GetUpdaterScopeForCommandLine(*base::CommandLine::ForCurrentProcess()); return GetUpdaterScopeForCommandLine(GetCommandLineLegacyCompatible());
} }
bool IsSystemInstall() { bool IsSystemInstall() {

@@ -70,6 +70,10 @@ IntegrationTest.Install tests that the updater can be installed on a clean OS,
that it is immediately active after installation, and then can be cleanly that it is immediately active after installation, and then can be cleanly
uninstalled. uninstalled.
IntegrationTest.Handoff tests that the updater can be installed on a clean OS,
that it can install an app via a "/handoff" command line, and then can be
cleanly uninstalled.
Overinstall cases are tested by IntegrationTest.OverinstallWorking and Overinstall cases are tested by IntegrationTest.OverinstallWorking and
IntegrationTest.OverinstallBroken, to ensure that the updater can be installed IntegrationTest.OverinstallBroken, to ensure that the updater can be installed
on an unclean OS, and as a post-condition of installation, the system has a on an unclean OS, and as a post-condition of installation, the system has a