Commit 6867d7c1 authored by Joshua Pawlicki's avatar Joshua Pawlicki Committed by Chromium LUCI CQ

Updater: pick up activity signals from managed applications.

Also introduces an in-process test server.
Also increases logging levels on the mac server.
Also adjusts NSUserDefaults domain to allow test and app code to communicate.

Bug: 1142591, 1119857
Fixed: 1142591, 1119857
Change-Id: Ic4f2f16924aba95a01186f07f488a0e49322fd87
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2518214
Commit-Queue: Joshua Pawlicki <waffles@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarSorin Jianu <sorin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#840616}
parent 2abcb881
......@@ -81,6 +81,9 @@ if (is_win || is_mac) {
# Use this source set for code which has platform-specific modules.
source_set("lib") {
sources = [
"activity.cc",
"activity.h",
"activity_impl.h",
"app/app.cc",
"app/app.h",
"app/app_install.cc",
......@@ -124,6 +127,7 @@ if (is_win || is_mac) {
if (is_mac) {
sources += [
"activity_impl_mac.cc",
"app/app_install_mac.mm",
"app/server/mac/app_server.h",
"app/server/mac/server.h",
......@@ -150,6 +154,7 @@ if (is_win || is_mac) {
if (is_win) {
sources += [
"activity_impl_win.cc",
"app/app_install_win.cc",
"app/server/win/com_classes.cc",
"app/server/win/com_classes.h",
......@@ -270,6 +275,7 @@ if (is_win || is_mac) {
]
deps = [
":version_header",
"//base",
"//components/update_client",
]
......@@ -308,6 +314,8 @@ if (is_win || is_mac) {
"tag_unittest.cc",
"test/integration_tests.cc",
"test/integration_tests.h",
"test/server.cc",
"test/server.h",
"unittest_util_unittest.cc",
"update_service_internal_impl_unittest.cc",
"update_service_unittest.cc",
......@@ -329,7 +337,9 @@ if (is_win || is_mac) {
"//chrome/updater/tools:unittest",
"//components/prefs:test_support",
"//components/update_client",
"//net:test_support",
"//testing/gtest",
"//third_party/re2",
"//url",
]
......
......@@ -13,5 +13,6 @@ include_rules = [
"+crypto",
"+third_party/boringssl",
"+third_party/crashpad",
"+third_party/re2",
"+third_party/zlib/google",
]
// 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 "chrome/updater/activity.h"
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chrome/updater/activity_impl.h"
namespace updater {
namespace {
constexpr int kDaysUnknown = -2;
}
ActivityDataService::ActivityDataService(bool is_machine)
: is_machine_(is_machine) {}
void ActivityDataService::GetActiveBits(
const std::vector<std::string>& ids,
base::OnceCallback<void(const std::set<std::string>&)> callback) const {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(
[](const std::vector<std::string>& ids, bool is_machine) {
std::set<std::string> result;
for (const auto& id : ids) {
if (GetActiveBit(id, is_machine))
result.insert(id);
}
return result;
},
ids, is_machine_),
std::move(callback));
}
void ActivityDataService::GetAndClearActiveBits(
const std::vector<std::string>& ids,
base::OnceCallback<void(const std::set<std::string>&)> callback) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(
[](const std::vector<std::string>& ids, bool is_machine) {
std::set<std::string> result;
for (const auto& id : ids) {
if (GetActiveBit(id, is_machine))
result.insert(id);
ClearActiveBit(id, is_machine);
}
return result;
},
ids, is_machine_),
std::move(callback));
}
int ActivityDataService::GetDaysSinceLastActive(const std::string& id) const {
// The updater does not report DaysSince data, only DateLast data.
return kDaysUnknown;
}
int ActivityDataService::GetDaysSinceLastRollCall(const std::string& id) const {
// The updater does not report DaysSince data, only DateLast data.
return kDaysUnknown;
}
} // namespace updater
// 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.
#ifndef CHROME_UPDATER_ACTIVITY_H_
#define CHROME_UPDATER_ACTIVITY_H_
#include <set>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "components/update_client/activity_data_service.h"
namespace updater {
class ActivityDataService final : public update_client::ActivityDataService {
public:
explicit ActivityDataService(bool is_machine);
ActivityDataService(const ActivityDataService&) = delete;
ActivityDataService& operator=(const ActivityDataService&) = delete;
~ActivityDataService() override = default;
// update_client::ActivityDataService:
void GetActiveBits(const std::vector<std::string>& ids,
base::OnceCallback<void(const std::set<std::string>&)>
callback) const override;
void GetAndClearActiveBits(
const std::vector<std::string>& ids,
base::OnceCallback<void(const std::set<std::string>&)> callback) override;
int GetDaysSinceLastActive(const std::string& id) const override;
int GetDaysSinceLastRollCall(const std::string& id) const override;
private:
bool is_machine_;
};
} // namespace updater
#endif // CHROME_UPDATER_ACTIVITY_H_
// 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.
#ifndef CHROME_UPDATER_ACTIVITY_IMPL_H_
#define CHROME_UPDATER_ACTIVITY_IMPL_H_
#include <string>
namespace updater {
bool GetActiveBit(const std::string& id, bool is_machine_);
void ClearActiveBit(const std::string& id, bool is_machine_);
} // namespace updater
#endif // CHROME_UPDATER_ACTIVITY_IMPL_H_
// 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 "chrome/updater/activity_impl.h"
#include <string>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "chrome/updater/updater_version.h"
namespace updater {
namespace {
base::FilePath GetActiveFile(const std::string& id) {
return base::GetHomeDir()
.AppendASCII("Library")
.AppendASCII(COMPANY_SHORTNAME_STRING)
.AppendASCII(COMPANY_SHORTNAME_STRING "SoftwareUpdate")
.AppendASCII("Actives")
.AppendASCII(id);
}
} // namespace
bool GetActiveBit(const std::string& id, bool is_machine_) {
if (is_machine_) {
// TODO(crbug.com/1096654): Add support for the machine case. Machine
// installs must look for values in each home dir.
return false;
} else {
base::FilePath path = GetActiveFile(id);
return base::PathExists(path) && base::PathIsWritable(path);
}
}
void ClearActiveBit(const std::string& id, bool is_machine_) {
if (is_machine_) {
// TODO(crbug.com/1096654): Add support for the machine case. Machine
// installs must clear values in each home dir.
} else {
if (!base::DeleteFile(GetActiveFile(id)))
VLOG(2) << "Failed to clear activity bit at " << GetActiveFile(id);
}
}
} // namespace updater
// 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 "chrome/updater/activity.h"
#include <string>
#include "base/logging.h"
#include "base/strings/strcat.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "base/win/windows_types.h"
#include "chrome/updater/win/constants.h"
namespace updater {
namespace {
constexpr base::char16 kDidRun[] = L"dr";
base::string16 GetAppClientStateKey(const std::string& id) {
return base::ASCIIToUTF16(base::StrCat({CLIENT_STATE_KEY, id}));
}
} // namespace
bool GetActiveBit(const std::string& id, bool is_machine_) {
if (is_machine_) {
// TODO(crbug.com/1096654): Add support for the machine case. Machine
// installs must look for values under HKLM and under every HKU\<sid>.
return false;
} else {
// TODO(crbug/1159498): Standardize registry access.
base::win::RegKey key;
if (key.Open(HKEY_CURRENT_USER, GetAppClientStateKey(id).c_str(),
KEY_READ | KEY_WOW64_32KEY) == ERROR_SUCCESS) {
base::string16 value;
if (key.ReadValue(kDidRun, &value) == ERROR_SUCCESS && value == L"1")
return true;
}
return false;
}
}
void ClearActiveBit(const std::string& id, bool is_machine_) {
if (is_machine_) {
// TODO(crbug.com/1096654): Add support for the machine case. Machine
// installs must clear values under HKLM and under every HKU\<sid>.
} else {
// TODO(crbug/1159498): Standardize registry access.
base::win::RegKey key;
if (key.Open(HKEY_CURRENT_USER, GetAppClientStateKey(id).c_str(),
KEY_WRITE | KEY_WOW64_32KEY) == ERROR_SUCCESS) {
const LONG result = key.WriteValue(kDidRun, L"0");
if (result) {
VLOG(2) << "Failed to clear HKCU activity key for " << id << ": "
<< result;
}
} else {
VLOG(2) << "Failed to open HKCU activity key with write for " << id;
}
}
}
} // namespace updater
......@@ -7,6 +7,7 @@
#include <utility>
#include "base/version.h"
#include "build/build_config.h"
#include "chrome/updater/activity.h"
#include "chrome/updater/crx_downloader_factory.h"
#include "chrome/updater/external_constants.h"
#include "chrome/updater/patcher.h"
......@@ -38,9 +39,11 @@ const int kDelayOneHour = kDelayOneMinute * 60;
namespace updater {
// TODO(crbug.com/1096654): Add support for machine `activity_data_service_`.
Configurator::Configurator(std::unique_ptr<UpdaterPrefs> prefs)
: prefs_(std::move(prefs)),
external_constants_(CreateExternalConstants()),
activity_data_service_(std::make_unique<ActivityDataService>(false)),
unzip_factory_(base::MakeRefCounted<UnzipperFactory>()),
patch_factory_(base::MakeRefCounted<PatcherFactory>()) {}
Configurator::~Configurator() = default;
......@@ -149,7 +152,7 @@ PrefService* Configurator::GetPrefService() const {
update_client::ActivityDataService* Configurator::GetActivityDataService()
const {
return nullptr;
return activity_data_service_.get();
}
bool Configurator::IsPerUserInstall() const {
......
......@@ -29,6 +29,7 @@ class ProtocolHandlerFactory;
namespace updater {
class ActivityDataService;
class UpdaterPrefs;
class ExternalConstants;
......@@ -75,6 +76,7 @@ class Configurator : public update_client::Configurator {
std::unique_ptr<UpdaterPrefs> prefs_;
std::unique_ptr<ExternalConstants> external_constants_;
std::unique_ptr<ActivityDataService> activity_data_service_;
scoped_refptr<update_client::NetworkFetcherFactory> network_fetcher_factory_;
scoped_refptr<update_client::CrxDownloaderFactory> crx_downloader_factory_;
scoped_refptr<update_client::UnzipperFactory> unzip_factory_;
......
......@@ -4,6 +4,8 @@
#include "chrome/updater/constants.h"
#include "chrome/updater/updater_version.h"
namespace updater {
// App ids.
......@@ -66,4 +68,9 @@ const char kProxyModeSystem[] = "system";
// Specifies that urls that can be cached by proxies are preferred.
const char kDownloadPreferenceCacheable[] = "cacheable";
#if defined(OS_MAC)
// The user defaults suite name.
const char kUserDefaultsSuiteName[] = MAC_BUNDLE_IDENTIFIER_STRING ".defaults";
#endif // defined(OS_MAC)
} // namespace updater
......@@ -153,6 +153,11 @@ constexpr int kWaitForInstallerProgressSec = 1;
constexpr int kWaitForLaunchctlUpdateSec = 5;
#endif // defined(OS_MAC)
#if defined(OS_MAC)
// The user defaults suite name.
extern const char kUserDefaultsSuiteName[];
#endif // defined(OS_MAC)
// Install Errors.
//
// Specific install errors for the updater are reported in such a way that
......
......@@ -13,11 +13,15 @@
#include "chrome/updater/updater_version.h"
#include "url/gurl.h"
#include "base/logging.h"
namespace updater {
std::vector<GURL> DevOverrideProvider::UpdateURL() const {
@autoreleasepool {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSUserDefaults* userDefaults = [[NSUserDefaults alloc]
initWithSuiteName:[NSString
stringWithUTF8String:kUserDefaultsSuiteName]];
NSURL* url = [userDefaults
URLForKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]];
if (url == nil)
......@@ -28,7 +32,9 @@ std::vector<GURL> DevOverrideProvider::UpdateURL() const {
bool DevOverrideProvider::UseCUP() const {
@autoreleasepool {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSUserDefaults* userDefaults = [[NSUserDefaults alloc]
initWithSuiteName:[NSString
stringWithUTF8String:kUserDefaultsSuiteName]];
id use_cup = [userDefaults
objectForKey:[NSString stringWithUTF8String:kDevOverrideKeyUseCUP]];
if (use_cup)
......
......@@ -20,7 +20,9 @@ namespace {
void ClearUserDefaults() {
@autoreleasepool {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSUserDefaults* userDefaults = [[NSUserDefaults alloc]
initWithSuiteName:[NSString
stringWithUTF8String:kUserDefaultsSuiteName]];
[userDefaults
removeObjectForKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]];
[userDefaults
......@@ -43,7 +45,9 @@ TEST_F(DevOverrideTest, TestDevOverrides) {
std::unique_ptr<ExternalConstants> consts = CreateExternalConstants();
@autoreleasepool {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSUserDefaults* userDefaults = [[NSUserDefaults alloc]
initWithSuiteName:[NSString
stringWithUTF8String:kUserDefaultsSuiteName]];
[userDefaults setURL:[NSURL URLWithString:@"http://localhost:8080"]
forKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]];
[userDefaults
......
......@@ -37,7 +37,8 @@ namespace updater {
namespace {
constexpr char kLoggingModuleSwitchValue[] = "*/updater/*=2";
constexpr char kLoggingModuleSwitchValue[] =
"*/updater/*=2,*/update_client/*=2";
#pragma mark Helpers
const base::FilePath GetUpdaterAppName() {
......
......@@ -10,21 +10,28 @@
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/strings/strcat.h"
#include "base/test/bind.h"
#include "base/test/scoped_run_loop_timeout.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "base/version.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/server.h"
#include "chrome/updater/test/test_app/constants.h"
#include "chrome/updater/test/test_app/test_app_version.h"
#include "chrome/updater/update_service.h"
#include "chrome/updater/updater_version.h"
#include "chrome/updater/util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace updater {
......@@ -42,6 +49,25 @@ void ExpectActiveVersion(std::string expected) {
EXPECT_EQ(CreateGlobalPrefs()->GetActiveVersion(), expected);
}
#if defined(OS_MAC)
void RegisterApp(const std::string& app_id) {
scoped_refptr<UpdateService> update_service = CreateUpdateService();
RegistrationRequest registration;
registration.app_id = app_id;
registration.version = base::Version("0.1");
base::RunLoop loop;
update_service->RegisterApp(
registration, base::BindOnce(base::BindLambdaForTesting(
[&loop](const RegistrationResponse& response) {
EXPECT_EQ(response.status_code, 0);
loop.Quit();
})));
loop.Run();
}
#endif // defined(OS_MAC)
} // namespace
void PrintLog() {
std::string contents;
VLOG(0) << GetDataDirPath().AppendASCII("updater.log");
......@@ -53,7 +79,6 @@ void PrintLog() {
VLOG(0) << "Failed to read updater.log file.";
}
}
} // namespace
const testing::TestInfo* GetTestInfo() {
return testing::UnitTest::GetInstance()->current_test_info();
......@@ -152,7 +177,7 @@ class IntegrationTest : public ::testing::Test {
void SetUp() override {
Clean();
ExpectClean();
EnterTestMode();
EnterTestMode(GURL("http://localhost:1234"));
}
void TearDown() override {
......@@ -200,6 +225,7 @@ TEST_F(IntegrationTest, SelfUninstallOutdatedUpdater) {
}
#if defined(OS_MAC)
// TODO(crbug.com/1163524): Enable on Windows.
TEST_F(IntegrationTest, RegisterTestApp) {
RegisterTestApp();
ExpectInstalled();
......@@ -208,6 +234,45 @@ TEST_F(IntegrationTest, RegisterTestApp) {
Uninstall();
}
// TODO(crbug.com/1163524): Enable on Windows.
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::TimeDelta::FromSeconds(18));
ScopedServer test_server;
Install();
ExpectInstalled();
// Register apps test1 and test2. Expect registration pings for each.
// TODO(crbug.com/1159525): Registration pings are currently not being sent.
RegisterApp("test1");
RegisterApp("test2");
// Set test1 to be active and do a background updatecheck.
SetActive("test1");
ExpectActive("test1");
ExpectNotActive("test2");
test_server.ExpectOnce(
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, UnregisterUninstalledApp) {
RegisterTestApp();
ExpectInstalled();
......
......@@ -5,6 +5,8 @@
#ifndef CHROME_UPDATER_TEST_INTEGRATION_TESTS_H_
#define CHROME_UPDATER_TEST_INTEGRATION_TESTS_H_
#include <string>
#include "build/build_config.h"
namespace base {
......@@ -13,9 +15,14 @@ class FilePath;
class Version;
} // namespace base
class GURL;
namespace updater {
namespace test {
// Prints the updater.log file to stdout.
void PrintLog();
// Removes traces of the updater from the system. It is best to run this at the
// start of each test in case a previous crash or timeout on the machine running
// the test left the updater in an installed or partially installed state.
......@@ -26,8 +33,9 @@ void Clean();
// test.
void ExpectClean();
// Places the updater into test mode (use local servers and disable CUP).
void EnterTestMode();
// Places the updater into test mode (use `url` as the update server and disable
// CUP).
void EnterTestMode(const GURL& url);
// Copies the logs to a location where they can be retrieved by ResultDB.
void CopyLog(const base::FilePath& src_dir);
......@@ -56,7 +64,7 @@ void ExpectActive();
void Uninstall();
// Runs the wake client and wait for it to exit. Assert that it exits with
// |exit_code|. The server should exit a few seconds after.
// `exit_code`. The server should exit a few seconds after.
void RunWake(int exit_code);
// Registers the test app. As a result, the bundled updater is installed,
......@@ -88,6 +96,15 @@ void SetupFakeUpdaterHigherVersion();
// Expects that this version of updater is uninstalled from the system.
void ExpectCandidateUninstalled();
// Sets the active bit for `app_id`.
void SetActive(const std::string& app_id);
// Expects that the active bit for `app_id` is set.
void ExpectActive(const std::string& app_id);
// Expects that the active bit for `app_id` is unset.
void ExpectNotActive(const std::string& app_id);
#if defined(OS_WIN)
void ExpectInterfacesRegistered();
#endif
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include <stdint.h>
#include <string>
#include "base/command_line.h"
#include "base/files/file_path.h"
......@@ -25,6 +26,7 @@
#include "chrome/updater/updater_version.h"
#include "chrome/updater/util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace updater {
namespace test {
......@@ -73,6 +75,15 @@ base::FilePath GetProductPath() {
.AppendASCII(PRODUCT_FULLNAME_STRING);
}
base::FilePath GetActiveFile(const std::string& id) {
return base::GetHomeDir()
.AppendASCII("Library")
.AppendASCII(COMPANY_SHORTNAME_STRING)
.AppendASCII(COMPANY_SHORTNAME_STRING "SoftwareUpdate")
.AppendASCII("Actives")
.AppendASCII(id);
}
void ExpectServiceAbsent(const std::string& service) {
bool success = false;
base::RunLoop loop;
......@@ -88,6 +99,25 @@ void ExpectServiceAbsent(const std::string& service) {
} // namespace
#endif // defined(COMPONENT_BUILD
void EnterTestMode(const GURL& url) {
@autoreleasepool {
NSUserDefaults* userDefaults = [[NSUserDefaults alloc]
initWithSuiteName:[NSString
stringWithUTF8String:kUserDefaultsSuiteName]];
[userDefaults
setURL:[NSURL URLWithString:base::SysUTF8ToNSString(url.spec())]
forKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]];
[userDefaults
setBool:NO
forKey:[NSString stringWithUTF8String:kDevOverrideKeyUseCUP]];
}
}
// crbug.com/1112527: These tests are not compatible with component build.
#if !defined(COMPONENT_BUILD)
base::FilePath GetDataDirPath() {
return base::mac::GetUserLibraryPath()
.AppendASCII("Application Support")
......@@ -107,7 +137,9 @@ void Clean() {
EXPECT_TRUE(base::DeletePathRecursively(GetDataDirPath()));
@autoreleasepool {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSUserDefaults* userDefaults = [[NSUserDefaults alloc]
initWithSuiteName:[NSString
stringWithUTF8String:kUserDefaultsSuiteName]];
[userDefaults
removeObjectForKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]];
[userDefaults
......@@ -138,18 +170,6 @@ void ExpectClean() {
ExpectServiceAbsent(kUpdateServiceInternalLaunchdName);
}
void EnterTestMode() {
// TODO(crbug.com/1119857): Point this to an actual fake server.
@autoreleasepool {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setURL:[NSURL URLWithString:@"http://localhost:8367"]
forKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]];
[userDefaults
setBool:NO
forKey:[NSString stringWithUTF8String:kDevOverrideKeyUseCUP]];
}
}
void ExpectInstalled() {
// Files must exist on the file system.
EXPECT_TRUE(base::PathExists(GetProductPath()));
......@@ -200,6 +220,8 @@ void ExpectCandidateUninstalled() {
}
void Uninstall() {
if (::testing::Test::HasFailure())
PrintLog();
// Copy logs from GetDataDirPath() before updater uninstalls itself
// and deletes the path.
CopyLog(GetDataDirPath());
......@@ -218,6 +240,26 @@ base::FilePath GetFakeUpdaterInstallFolderPath(const base::Version& version) {
return GetExecutableFolderPathForVersion(version);
}
void SetActive(const std::string& app_id) {
base::File::Error err = base::File::FILE_OK;
base::FilePath actives_file = GetActiveFile(app_id);
EXPECT_TRUE(base::CreateDirectoryAndGetError(actives_file.DirName(), &err))
<< "Error: " << err;
EXPECT_TRUE(base::WriteFile(actives_file, ""));
}
void ExpectActive(const std::string& app_id) {
base::FilePath path = GetActiveFile(app_id);
EXPECT_TRUE(base::PathExists(path) && base::PathIsWritable(path))
<< app_id << " is not active";
}
void ExpectNotActive(const std::string& app_id) {
base::FilePath path = GetActiveFile(app_id);
EXPECT_FALSE(base::PathExists(path) && base::PathIsWritable(path))
<< app_id << " is active.";
}
#endif // !defined(COMPONENT_BUILD)
} // namespace test
......
......@@ -3,11 +3,14 @@
// found in the LICENSE file.
#include <wrl/client.h>
#include <string>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/task_traits.h"
......@@ -23,11 +26,14 @@
#include "chrome/updater/util.h"
#include "chrome/updater/win/constants.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace updater {
namespace test {
namespace {
constexpr base::char16 kDidRun[] = L"dr";
base::FilePath GetInstallerPath() {
base::FilePath test_executable;
if (!base::PathService::Get(base::FILE_EXE, &test_executable))
......@@ -44,6 +50,10 @@ base::FilePath GetProductPath() {
.AppendASCII(UPDATER_VERSION_STRING);
}
base::string16 GetAppClientStateKey(const std::string& id) {
return base::ASCIIToUTF16(base::StrCat({CLIENT_STATE_KEY, id}));
}
} // namespace
base::FilePath GetInstalledExecutablePath() {
......@@ -88,13 +98,12 @@ void ExpectClean() {
EXPECT_FALSE(base::PathExists(GetDataDirPath()));
}
void EnterTestMode() {
// TODO(crbug.com/1119857): Point this to an actual fake server.
void EnterTestMode(const GURL& url) {
base::win::RegKey key(HKEY_CURRENT_USER, L"", KEY_SET_VALUE);
ASSERT_EQ(key.Create(HKEY_CURRENT_USER, UPDATE_DEV_KEY, KEY_WRITE),
ERROR_SUCCESS);
ASSERT_EQ(key.WriteValue(base::UTF8ToUTF16(kDevOverrideKeyUrl).c_str(),
L"http://localhost:8367"),
base::UTF8ToUTF16(url.spec()).c_str()),
ERROR_SUCCESS);
ASSERT_EQ(key.WriteValue(base::UTF8ToUTF16(kDevOverrideKeyUseCUP).c_str(),
DWORD{0}),
......@@ -139,6 +148,8 @@ void Install() {
}
void Uninstall() {
if (::testing::Test::HasFailure())
PrintLog();
// Copy logs from GetDataDirPath() before updater uninstalls itself
// and deletes the path.
CopyLog(GetDataDirPath());
......@@ -160,6 +171,37 @@ void Uninstall() {
SleepFor(5);
}
void SetActive(const std::string& id) {
// TODO(crbug/1159498): Standardize registry access.
base::win::RegKey key;
ASSERT_EQ(key.Open(HKEY_CURRENT_USER, GetAppClientStateKey(id).c_str(),
KEY_WRITE | KEY_WOW64_32KEY),
ERROR_SUCCESS);
EXPECT_EQ(key.WriteValue(kDidRun, L"1"), ERROR_SUCCESS);
}
void ExpectActive(const std::string& id) {
// TODO(crbug/1159498): Standardize registry access.
base::win::RegKey key;
ASSERT_EQ(key.Open(HKEY_CURRENT_USER, GetAppClientStateKey(id).c_str(),
KEY_READ | KEY_WOW64_32KEY),
ERROR_SUCCESS);
base::string16 value;
ASSERT_EQ(key.ReadValue(kDidRun, &value), ERROR_SUCCESS);
EXPECT_EQ(value, L"1");
}
void ExpectNotActive(const std::string& id) {
// TODO(crbug/1159498): Standardize registry access.
base::win::RegKey key;
if (key.Open(HKEY_CURRENT_USER, GetAppClientStateKey(id).c_str(),
KEY_READ | KEY_WOW64_32KEY) == ERROR_SUCCESS) {
base::string16 value;
if (key.ReadValue(kDidRun, &value) == ERROR_SUCCESS)
EXPECT_EQ(value, L"0");
}
}
// Tests if the typelibs and some of the public, internal, and
// legacy interfaces are available. Failure to query these interfaces indicates
// an issue with typelib registration.
......
// 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 "chrome/updater/test/server.h"
#include <list>
#include <memory>
#include <string>
#include "chrome/updater/test/integration_tests.h"
#include "net/http/http_status_code.h"
#include "net/test/embedded_test_server/embedded_test_server.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 "third_party/re2/src/re2/re2.h"
namespace updater {
namespace test {
ScopedServer::ScopedServer()
: test_server_(std::make_unique<net::test_server::EmbeddedTestServer>()) {
test_server_->RegisterRequestHandler(base::BindRepeating(
&ScopedServer::HandleRequest, base::Unretained(this)));
EXPECT_TRUE((test_server_handle_ = test_server_->StartAndReturnHandle()));
EnterTestMode(test_server_->base_url());
}
ScopedServer::~ScopedServer() {
for (const auto& regex : request_body_regexes_) {
ADD_FAILURE() << "Unmet expectation: " << regex;
}
}
void ScopedServer::ExpectOnce(const std::string& request_body_regex,
const std::string& response_body) {
request_body_regexes_.push_back(request_body_regex);
response_bodies_.push_back(response_body);
}
std::unique_ptr<net::test_server::HttpResponse> ScopedServer::HandleRequest(
const net::test_server::HttpRequest& request) {
if (request_body_regexes_.empty()) {
ADD_FAILURE() << "Unexpected request with body: " << request.content;
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_code(net::HTTP_INTERNAL_SERVER_ERROR);
return response;
}
if (!re2::RE2::PartialMatch(request.content, request_body_regexes_.front())) {
ADD_FAILURE() << "Request with body: " << request.content
<< " did not match expected regex "
<< request_body_regexes_.front();
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_code(net::HTTP_INTERNAL_SERVER_ERROR);
return response;
}
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_code(net::HTTP_OK);
response->set_content(response_bodies_.front());
request_body_regexes_.pop_front();
response_bodies_.pop_front();
return response;
}
} // namespace test
} // namespace updater
// 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.
#ifndef CHROME_UPDATER_TEST_SERVER_H_
#define CHROME_UPDATER_TEST_SERVER_H_
#include <list>
#include <memory>
#include <string>
#include "net/test/embedded_test_server/embedded_test_server.h"
namespace net {
namespace test_server {
struct HttpRequest;
class HttpResponse;
} // namespace test_server
} // namespace net
namespace updater {
namespace test {
class ScopedServer {
public:
// Creates and starts a scoped server. Sets up the updater to communicate
// with it. Multiple scoped servers are not allowed.
ScopedServer();
// Shuts down the server and verifies that all expectations were met and that
// no extra communications were received.
~ScopedServer();
ScopedServer(const ScopedServer&) = delete;
ScopedServer& operator=(const ScopedServer&) = delete;
// Registers an expected request with the server. Requests must match the
// expectation regexes in the order the expectations were set. The server
// replies with an HTTP 200 and `response_body` to an expected request. It
// replies with HTTP 500 and fails the test if a request does not match the
// next expected `request_body_regex`, or if there are no more expected
// requests. If the server does not receive every expected request, it will
// fail the test during destruction.
void ExpectOnce(const std::string& request_body_regex,
const std::string& response_body);
private:
std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
const net::test_server::HttpRequest& request);
std::unique_ptr<net::test_server::EmbeddedTestServer> test_server_;
net::test_server::EmbeddedTestServerHandle test_server_handle_;
std::list<std::string> request_body_regexes_;
std::list<std::string> response_bodies_;
};
} // namespace test
} // namespace updater
#endif // CHROME_UPDATER_TEST_SERVER_H_
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