blob: 0617d52290424f1aeae056ba3493afefc5782131 [file] [log] [blame]
// Copyright 2020 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 <cstdlib>
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/numerics/checked_math.h"
#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_run_loop_timeout.h"
#include "base/test/task_environment.h"
#include "base/test/test_timeouts.h"
#include "base/time/time.h"
#include "base/version.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "chrome/updater/constants.h"
#include "chrome/updater/persisted_data.h"
#include "chrome/updater/prefs.h"
#include "chrome/updater/registration_data.h"
#include "chrome/updater/test/integration_test_commands.h"
#include "chrome/updater/test/integration_tests_impl.h"
#include "chrome/updater/test/server.h"
#include "chrome/updater/update_service.h"
#include "chrome/updater/updater_scope.h"
#include "chrome/updater/updater_version.h"
#include "chrome/updater/util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#if defined(OS_WIN)
#include <shlobj.h>
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "chrome/updater/app/server/win/updater_legacy_idl.h"
#include "chrome/updater/win/win_constants.h"
#include "chrome/updater/win/win_util.h"
#endif // OS_WIN
namespace updater {
namespace test {
namespace {
#if defined(OS_WIN) || !defined(COMPONENT_BUILD)
void ExpectNoUpdateSequence(ScopedServer* test_server,
const std::string& app_id) {
test_server->ExpectOnce(
{base::BindRepeating(
RequestMatcherRegex,
base::StringPrintf(R"(.*"appid":"%s".*)", app_id.c_str()))},
base::StringPrintf(")]}'\n"
R"({"response":{)"
R"( "protocol":"3.1",)"
R"( "app":[)"
R"( {)"
R"( "appid":"%s",)"
R"( "status":"ok",)"
R"( "updatecheck":{)"
R"( "status":"noupdate")"
R"( })"
R"( })"
R"( ])"
R"(}})",
app_id.c_str()));
}
#endif // defined(OS_WIN) || !defined(COMPONENT_BUILD)
} // namespace
class IntegrationTest : public ::testing::Test {
public:
IntegrationTest() : test_commands_(CreateIntegrationTestCommands()) {}
~IntegrationTest() override = default;
protected:
void SetUp() override {
logging::SetLogItems(true, // enable_process_id
true, // enable_thread_id
true, // enable_timestamp
false); // enable_tickcount
Clean();
ExpectClean();
// TODO(crbug.com/1233612) - reenable the code when system tests pass.
// SetUpTestService();
EnterTestMode(GURL("http://localhost:1234"));
}
void TearDown() override {
ExpectClean();
PrintLog();
// TODO(crbug.com/1159189): Use a specific test output directory
// because Uninstall() deletes the files under GetDataDirPath().
CopyLog();
// TODO(crbug.com/1233612) - reenable the code when system tests pass.
// TearDownTestService();
Clean();
}
void CopyLog() { test_commands_->CopyLog(); }
void PrintLog() { test_commands_->PrintLog(); }
void Install() { test_commands_->Install(); }
void ExpectInstalled() { test_commands_->ExpectInstalled(); }
void Uninstall() {
PrintLog();
CopyLog();
test_commands_->Uninstall();
WaitForUpdaterExit();
}
void ExpectCandidateUninstalled() {
test_commands_->ExpectCandidateUninstalled();
}
void Clean() { test_commands_->Clean(); }
void ExpectClean() { test_commands_->ExpectClean(); }
void EnterTestMode(const GURL& url) { test_commands_->EnterTestMode(url); }
void ExpectVersionActive(const std::string& version) {
test_commands_->ExpectVersionActive(version);
}
void ExpectVersionNotActive(const std::string& version) {
test_commands_->ExpectVersionNotActive(version);
}
void ExpectActiveUpdater() { test_commands_->ExpectActiveUpdater(); }
#if defined(OS_WIN)
void ExpectInterfacesRegistered() {
test_commands_->ExpectInterfacesRegistered();
}
void ExpectLegacyUpdate3WebSucceeds(const std::string& app_id,
int expected_final_state,
int expected_error_code) {
test_commands_->ExpectLegacyUpdate3WebSucceeds(app_id, expected_final_state,
expected_error_code);
}
void ExpectLegacyProcessLauncherSucceeds() {
test_commands_->ExpectLegacyProcessLauncherSucceeds();
}
void RunUninstallCmdLine() { test_commands_->RunUninstallCmdLine(); }
#endif // OS_WIN
void SetupFakeUpdaterHigherVersion() {
test_commands_->SetupFakeUpdaterHigherVersion();
}
void SetupFakeUpdaterLowerVersion() {
test_commands_->SetupFakeUpdaterLowerVersion();
}
void SetActive(const std::string& app_id) {
test_commands_->SetActive(app_id);
}
void ExpectActive(const std::string& app_id) {
test_commands_->ExpectActive(app_id);
}
void ExpectNotActive(const std::string& app_id) {
test_commands_->ExpectNotActive(app_id);
}
void SetExistenceCheckerPath(const std::string& app_id,
const base::FilePath& path) {
test_commands_->SetExistenceCheckerPath(app_id, path);
}
void SetServerStarts(int value) { test_commands_->SetServerStarts(value); }
void ExpectAppUnregisteredExistenceCheckerPath(const std::string& app_id) {
test_commands_->ExpectAppUnregisteredExistenceCheckerPath(app_id);
}
void ExpectAppVersion(const std::string& app_id,
const base::Version& version) {
test_commands_->ExpectAppVersion(app_id, version);
}
void RegisterApp(const std::string& app_id) {
test_commands_->RegisterApp(app_id);
}
void RunWake(int exit_code) { test_commands_->RunWake(exit_code); }
void Update(const std::string& app_id) { test_commands_->Update(app_id); }
void UpdateAll() { test_commands_->UpdateAll(); }
base::FilePath GetDifferentUserPath() {
return test_commands_->GetDifferentUserPath();
}
void WaitForUpdaterExit() { test_commands_->WaitForUpdaterExit(); }
void SetUpTestService() {
#if defined(OS_WIN)
test_commands_->SetUpTestService();
#endif // OS_WIN
}
void TearDownTestService() {
#if defined(OS_WIN)
test_commands_->TearDownTestService();
#endif // OS_WIN
}
void ExpectUpdateSequence(ScopedServer* test_server,
const std::string& app_id,
const base::Version& from_version,
const base::Version& to_version) {
test_commands_->ExpectUpdateSequence(test_server, app_id, from_version,
to_version);
}
void ExpectRegistrationEvent(ScopedServer* test_server,
const std::string& app_id) {
test_server->ExpectOnce(
{base::BindRepeating(
RequestMatcherRegex,
base::StrCat({R"(.*"appid":")", app_id, R"(","enabled":true,")",
R"(event":\[{"eventresult":1,"eventtype":2,.*)"}))},
"");
}
void StressUpdateService() { test_commands_->StressUpdateService(); }
void CallServiceUpdate(
const std::string& app_id,
UpdateService::PolicySameVersionUpdate policy_same_version_update) {
test_commands_->CallServiceUpdate(app_id, policy_same_version_update);
}
scoped_refptr<IntegrationTestCommands> test_commands_;
private:
base::test::TaskEnvironment environment_;
};
// The project's position is that component builds are not portable outside of
// the build directory. Therefore, installation of component builds is not
// expected to work and these tests do not run on component builders.
// See crbug.com/1112527.
#if defined(OS_WIN) || !defined(COMPONENT_BUILD)
TEST_F(IntegrationTest, InstallUninstall) {
Install();
WaitForUpdaterExit();
ExpectInstalled();
ExpectVersionActive(kUpdaterVersion);
ExpectActiveUpdater();
#if defined(OS_WIN)
// Tests the COM registration after the install. For now, tests that the
// COM interfaces are registered, which is indirectly testing the type
// library separation for the public, private, and legacy interfaces.
ExpectInterfacesRegistered();
#endif // OS_WIN
Uninstall();
}
TEST_F(IntegrationTest, SelfUninstallOutdatedUpdater) {
Install();
ExpectInstalled();
WaitForUpdaterExit();
SetupFakeUpdaterHigherVersion();
ExpectVersionNotActive(kUpdaterVersion);
RunWake(0);
WaitForUpdaterExit();
ExpectCandidateUninstalled();
// The candidate uninstall should not have altered global prefs.
ExpectVersionNotActive(kUpdaterVersion);
ExpectVersionNotActive("0.0.0.0");
Uninstall();
Clean();
}
TEST_F(IntegrationTest, QualifyUpdater) {
ScopedServer test_server(test_commands_);
ExpectRegistrationEvent(&test_server, kUpdaterAppId);
Install();
ExpectInstalled();
WaitForUpdaterExit();
SetupFakeUpdaterLowerVersion();
ExpectVersionNotActive(kUpdaterVersion);
ExpectRegistrationEvent(&test_server, kQualificationAppId);
ExpectUpdateSequence(&test_server, kQualificationAppId, base::Version("0.1"),
base::Version("0.2"));
RunWake(0);
WaitForUpdaterExit();
// This instance is now qualified and should activate itself and check itself
// for updates on the next check.
test_server.ExpectOnce(
{base::BindRepeating(RequestMatcherRegex,
base::StringPrintf(".*%s.*", kUpdaterAppId))},
")]}'\n");
RunWake(0);
WaitForUpdaterExit();
ExpectVersionActive(kUpdaterVersion);
Uninstall();
Clean();
}
TEST_F(IntegrationTest, SelfUpdate) {
ScopedServer test_server(test_commands_);
ExpectRegistrationEvent(&test_server, kUpdaterAppId);
Install();
base::Version next_version(base::StringPrintf("%s1", kUpdaterVersion));
ExpectUpdateSequence(&test_server, kUpdaterAppId,
base::Version(kUpdaterVersion), next_version);
RunWake(0);
WaitForUpdaterExit();
ExpectAppVersion(kUpdaterAppId, next_version);
Uninstall();
Clean();
}
TEST_F(IntegrationTest, ReportsActive) {
// A longer than usual timeout is needed for this test because the macOS
// UpdateServiceInternal server takes at least 10 seconds to shut down after
// Install, and RegisterApp cannot make progress until it shut downs and
// releases the global prefs lock. We give it at most 18 seconds to be safe.
base::test::ScopedRunLoopTimeout timeout(FROM_HERE, base::Seconds(18));
ScopedServer test_server(test_commands_);
ExpectRegistrationEvent(&test_server, kUpdaterAppId);
Install();
ExpectInstalled();
// Register apps test1 and test2. Expect registration pings for each.
ExpectRegistrationEvent(&test_server, "test1");
RegisterApp("test1");
ExpectRegistrationEvent(&test_server, "test2");
RegisterApp("test2");
// Set test1 to be active and do a background updatecheck.
SetActive("test1");
ExpectActive("test1");
ExpectNotActive("test2");
test_server.ExpectOnce(
{base::BindRepeating(
RequestMatcherRegex,
R"(.*"appid":"test1","enabled":true,"ping":{"a":-2,.*)")},
R"()]}')"
"\n"
R"({"response":{"protocol":"3.1","daystart":{"elapsed_)"
R"(days":5098}},"app":[{"appid":"test1","status":"ok",)"
R"("updatecheck":{"status":"noupdate"}},{"appid":"test2",)"
R"("status":"ok","updatecheck":{"status":"noupdate"}}]})");
RunWake(0);
// The updater has cleared the active bits.
ExpectNotActive("test1");
ExpectNotActive("test2");
Uninstall();
}
TEST_F(IntegrationTest, UpdateApp) {
ScopedServer test_server(test_commands_);
ExpectRegistrationEvent(&test_server, kUpdaterAppId);
Install();
const std::string kAppId("test");
ExpectRegistrationEvent(&test_server, kAppId);
RegisterApp(kAppId);
base::Version v1("1");
ExpectUpdateSequence(&test_server, kAppId, base::Version("0.1"), v1);
RunWake(0);
base::Version v2("2");
ExpectUpdateSequence(&test_server, kAppId, v1, v2);
Update(kAppId);
WaitForUpdaterExit();
ExpectAppVersion(kAppId, v2);
Uninstall();
Clean();
}
TEST_F(IntegrationTest, MultipleWakesOneNetRequest) {
ScopedServer test_server(test_commands_);
ExpectRegistrationEvent(&test_server, kUpdaterAppId);
Install();
// Only one sequence visible to the server despite multiple wakes.
ExpectNoUpdateSequence(&test_server, kUpdaterAppId);
RunWake(0);
RunWake(0);
Uninstall();
Clean();
}
TEST_F(IntegrationTest, MultipleUpdateAllsMultipleNetRequests) {
ScopedServer test_server(test_commands_);
ExpectRegistrationEvent(&test_server, kUpdaterAppId);
Install();
ExpectNoUpdateSequence(&test_server, kUpdaterAppId);
UpdateAll();
ExpectNoUpdateSequence(&test_server, kUpdaterAppId);
UpdateAll();
Uninstall();
Clean();
}
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
TEST_F(IntegrationTest, LegacyUpdate3Web) {
ScopedServer test_server(test_commands_);
ExpectRegistrationEvent(&test_server, kUpdaterAppId);
Install();
const char kAppId[] = "test1";
ExpectRegistrationEvent(&test_server, kAppId);
RegisterApp(kAppId);
ExpectNoUpdateSequence(&test_server, kAppId);
ExpectLegacyUpdate3WebSucceeds(kAppId, STATE_NO_UPDATE, S_OK);
ExpectUpdateSequence(&test_server, kAppId, base::Version("0.1"),
base::Version("0.2"));
ExpectLegacyUpdate3WebSucceeds(kAppId, STATE_INSTALL_COMPLETE, S_OK);
// TODO(crbug.com/1272853) - Need administrative access to be able to write
// under the policies key.
if (::IsUserAnAdmin()) {
base::win::RegKey key(HKEY_LOCAL_MACHINE, UPDATER_POLICIES_KEY,
Wow6432(KEY_ALL_ACCESS));
EXPECT_EQ(ERROR_SUCCESS,
key.WriteValue(
base::StrCat({L"Update", base::UTF8ToWide(kAppId)}).c_str(),
kPolicyAutomaticUpdatesOnly));
ExpectLegacyUpdate3WebSucceeds(
kAppId, STATE_ERROR, GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY_MANUAL);
EXPECT_EQ(ERROR_SUCCESS,
key.WriteValue(
base::StrCat({L"Update", base::UTF8ToWide(kAppId)}).c_str(),
static_cast<DWORD>(kPolicyDisabled)));
ExpectLegacyUpdate3WebSucceeds(kAppId, STATE_ERROR,
GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY);
EXPECT_EQ(ERROR_SUCCESS,
base::win::RegKey(HKEY_LOCAL_MACHINE, L"", Wow6432(DELETE))
.DeleteKey(UPDATER_POLICIES_KEY));
}
Uninstall();
}
TEST_F(IntegrationTest, LegacyProcessLauncher) {
Install();
ExpectLegacyProcessLauncherSucceeds();
Uninstall();
}
TEST_F(IntegrationTest, UninstallCmdLine) {
Install();
ExpectInstalled();
ExpectVersionActive(kUpdaterVersion);
ExpectActiveUpdater();
// Running the uninstall command does not uninstall this instance of the
// updater right after installing it (not enough server starts).
RunUninstallCmdLine();
WaitForUpdaterExit();
ExpectInstalled();
SetServerStarts(24);
// Uninstall the idle updater.
RunUninstallCmdLine();
WaitForUpdaterExit();
ExpectClean();
}
#endif // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
TEST_F(IntegrationTest, UnregisterUninstalledApp) {
Install();
ExpectInstalled();
RegisterApp("test1");
RegisterApp("test2");
WaitForUpdaterExit();
ExpectVersionActive(kUpdaterVersion);
ExpectActiveUpdater();
SetExistenceCheckerPath("test1", base::FilePath(FILE_PATH_LITERAL("NONE")));
RunWake(0);
WaitForUpdaterExit();
ExpectInstalled();
ExpectAppUnregisteredExistenceCheckerPath("test1");
Uninstall();
}
TEST_F(IntegrationTest, UninstallIfMaxServerWakesBeforeRegistrationExceeded) {
Install();
WaitForUpdaterExit();
ExpectInstalled();
SetServerStarts(24);
RunWake(0);
WaitForUpdaterExit();
ExpectClean();
}
#if defined(OS_MAC)
// TODO(https://crbug.com/1287235): Failing consistently on Mac.
#define MAYBE_UninstallUpdaterWhenAllAppsUninstalled \
DISABLED_UninstallUpdaterWhenAllAppsUninstalled
#else
#define MAYBE_UninstallUpdaterWhenAllAppsUninstalled \
UninstallUpdaterWhenAllAppsUninstalled
#endif
TEST_F(IntegrationTest, MAYBE_UninstallUpdaterWhenAllAppsUninstalled) {
Install();
RegisterApp("test1");
ExpectInstalled();
WaitForUpdaterExit();
RunWake(0);
WaitForUpdaterExit();
ExpectInstalled();
ExpectVersionActive(kUpdaterVersion);
ExpectActiveUpdater();
SetExistenceCheckerPath("test1", base::FilePath(FILE_PATH_LITERAL("NONE")));
RunWake(0);
WaitForUpdaterExit();
ExpectClean();
}
// Windows does not currently have a concept of app ownership, so this
// test need not run on Windows.
#if defined(OS_MAC)
TEST_F(IntegrationTest, UnregisterUnownedApp) {
Install();
ExpectInstalled();
ExpectVersionActive(kUpdaterVersion);
ExpectActiveUpdater();
RegisterApp("test1");
RegisterApp("test2");
SetExistenceCheckerPath("test1", GetDifferentUserPath());
RunWake(0);
WaitForUpdaterExit();
ExpectAppUnregisteredExistenceCheckerPath("test1");
Uninstall();
}
#endif // defined(OS_MAC)
TEST_F(IntegrationTest, UpdateServiceStress) {
Install();
ExpectInstalled();
StressUpdateService();
Uninstall();
}
TEST_F(IntegrationTest, SameVersionUpdate) {
ScopedServer test_server(test_commands_);
ExpectRegistrationEvent(&test_server, kUpdaterAppId);
Install();
ExpectInstalled();
const std::string app_id = "test-appid";
ExpectRegistrationEvent(&test_server, app_id);
RegisterApp(app_id);
const std::string response = base::StringPrintf(
")]}'\n"
R"({"response":{)"
R"( "protocol":"3.1",)"
R"( "app":[)"
R"( {)"
R"( "appid":"%s",)"
R"( "status":"ok",)"
R"( "updatecheck":{)"
R"( "status":"noupdate")"
R"( })"
R"( })"
R"( ])"
R"(}})",
app_id.c_str());
test_server.ExpectOnce(
{base::BindRepeating(
RequestMatcherRegex,
R"(.*"updatecheck":{"sameversionupdate":true},"version":"0.1"}.*)")},
response);
CallServiceUpdate(app_id, UpdateService::PolicySameVersionUpdate::kAllowed);
test_server.ExpectOnce(
{base::BindRepeating(RequestMatcherRegex,
R"(.*"updatecheck":{},"version":"0.1"}.*)")},
response);
CallServiceUpdate(app_id,
UpdateService::PolicySameVersionUpdate::kNotAllowed);
Uninstall();
}
#endif // defined(OS_WIN) || !defined(COMPONENT_BUILD)
} // namespace test
} // namespace updater