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) { ...@@ -81,6 +81,9 @@ if (is_win || is_mac) {
# Use this source set for code which has platform-specific modules. # Use this source set for code which has platform-specific modules.
source_set("lib") { source_set("lib") {
sources = [ sources = [
"activity.cc",
"activity.h",
"activity_impl.h",
"app/app.cc", "app/app.cc",
"app/app.h", "app/app.h",
"app/app_install.cc", "app/app_install.cc",
...@@ -124,6 +127,7 @@ if (is_win || is_mac) { ...@@ -124,6 +127,7 @@ if (is_win || is_mac) {
if (is_mac) { if (is_mac) {
sources += [ sources += [
"activity_impl_mac.cc",
"app/app_install_mac.mm", "app/app_install_mac.mm",
"app/server/mac/app_server.h", "app/server/mac/app_server.h",
"app/server/mac/server.h", "app/server/mac/server.h",
...@@ -150,6 +154,7 @@ if (is_win || is_mac) { ...@@ -150,6 +154,7 @@ if (is_win || is_mac) {
if (is_win) { if (is_win) {
sources += [ sources += [
"activity_impl_win.cc",
"app/app_install_win.cc", "app/app_install_win.cc",
"app/server/win/com_classes.cc", "app/server/win/com_classes.cc",
"app/server/win/com_classes.h", "app/server/win/com_classes.h",
...@@ -270,6 +275,7 @@ if (is_win || is_mac) { ...@@ -270,6 +275,7 @@ if (is_win || is_mac) {
] ]
deps = [ deps = [
":version_header",
"//base", "//base",
"//components/update_client", "//components/update_client",
] ]
...@@ -308,6 +314,8 @@ if (is_win || is_mac) { ...@@ -308,6 +314,8 @@ if (is_win || is_mac) {
"tag_unittest.cc", "tag_unittest.cc",
"test/integration_tests.cc", "test/integration_tests.cc",
"test/integration_tests.h", "test/integration_tests.h",
"test/server.cc",
"test/server.h",
"unittest_util_unittest.cc", "unittest_util_unittest.cc",
"update_service_internal_impl_unittest.cc", "update_service_internal_impl_unittest.cc",
"update_service_unittest.cc", "update_service_unittest.cc",
...@@ -329,7 +337,9 @@ if (is_win || is_mac) { ...@@ -329,7 +337,9 @@ if (is_win || is_mac) {
"//chrome/updater/tools:unittest", "//chrome/updater/tools:unittest",
"//components/prefs:test_support", "//components/prefs:test_support",
"//components/update_client", "//components/update_client",
"//net:test_support",
"//testing/gtest", "//testing/gtest",
"//third_party/re2",
"//url", "//url",
] ]
......
...@@ -13,5 +13,6 @@ include_rules = [ ...@@ -13,5 +13,6 @@ include_rules = [
"+crypto", "+crypto",
"+third_party/boringssl", "+third_party/boringssl",
"+third_party/crashpad", "+third_party/crashpad",
"+third_party/re2",
"+third_party/zlib/google", "+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 @@ ...@@ -7,6 +7,7 @@
#include <utility> #include <utility>
#include "base/version.h" #include "base/version.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/updater/activity.h"
#include "chrome/updater/crx_downloader_factory.h" #include "chrome/updater/crx_downloader_factory.h"
#include "chrome/updater/external_constants.h" #include "chrome/updater/external_constants.h"
#include "chrome/updater/patcher.h" #include "chrome/updater/patcher.h"
...@@ -38,9 +39,11 @@ const int kDelayOneHour = kDelayOneMinute * 60; ...@@ -38,9 +39,11 @@ const int kDelayOneHour = kDelayOneMinute * 60;
namespace updater { namespace updater {
// TODO(crbug.com/1096654): Add support for machine `activity_data_service_`.
Configurator::Configurator(std::unique_ptr<UpdaterPrefs> prefs) Configurator::Configurator(std::unique_ptr<UpdaterPrefs> prefs)
: prefs_(std::move(prefs)), : prefs_(std::move(prefs)),
external_constants_(CreateExternalConstants()), external_constants_(CreateExternalConstants()),
activity_data_service_(std::make_unique<ActivityDataService>(false)),
unzip_factory_(base::MakeRefCounted<UnzipperFactory>()), unzip_factory_(base::MakeRefCounted<UnzipperFactory>()),
patch_factory_(base::MakeRefCounted<PatcherFactory>()) {} patch_factory_(base::MakeRefCounted<PatcherFactory>()) {}
Configurator::~Configurator() = default; Configurator::~Configurator() = default;
...@@ -149,7 +152,7 @@ PrefService* Configurator::GetPrefService() const { ...@@ -149,7 +152,7 @@ PrefService* Configurator::GetPrefService() const {
update_client::ActivityDataService* Configurator::GetActivityDataService() update_client::ActivityDataService* Configurator::GetActivityDataService()
const { const {
return nullptr; return activity_data_service_.get();
} }
bool Configurator::IsPerUserInstall() const { bool Configurator::IsPerUserInstall() const {
......
...@@ -29,6 +29,7 @@ class ProtocolHandlerFactory; ...@@ -29,6 +29,7 @@ class ProtocolHandlerFactory;
namespace updater { namespace updater {
class ActivityDataService;
class UpdaterPrefs; class UpdaterPrefs;
class ExternalConstants; class ExternalConstants;
...@@ -75,6 +76,7 @@ class Configurator : public update_client::Configurator { ...@@ -75,6 +76,7 @@ class Configurator : public update_client::Configurator {
std::unique_ptr<UpdaterPrefs> prefs_; std::unique_ptr<UpdaterPrefs> prefs_;
std::unique_ptr<ExternalConstants> external_constants_; 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::NetworkFetcherFactory> network_fetcher_factory_;
scoped_refptr<update_client::CrxDownloaderFactory> crx_downloader_factory_; scoped_refptr<update_client::CrxDownloaderFactory> crx_downloader_factory_;
scoped_refptr<update_client::UnzipperFactory> unzip_factory_; scoped_refptr<update_client::UnzipperFactory> unzip_factory_;
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "chrome/updater/constants.h" #include "chrome/updater/constants.h"
#include "chrome/updater/updater_version.h"
namespace updater { namespace updater {
// App ids. // App ids.
...@@ -66,4 +68,9 @@ const char kProxyModeSystem[] = "system"; ...@@ -66,4 +68,9 @@ const char kProxyModeSystem[] = "system";
// Specifies that urls that can be cached by proxies are preferred. // Specifies that urls that can be cached by proxies are preferred.
const char kDownloadPreferenceCacheable[] = "cacheable"; 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 } // namespace updater
...@@ -153,6 +153,11 @@ constexpr int kWaitForInstallerProgressSec = 1; ...@@ -153,6 +153,11 @@ constexpr int kWaitForInstallerProgressSec = 1;
constexpr int kWaitForLaunchctlUpdateSec = 5; constexpr int kWaitForLaunchctlUpdateSec = 5;
#endif // defined(OS_MAC) #endif // defined(OS_MAC)
#if defined(OS_MAC)
// The user defaults suite name.
extern const char kUserDefaultsSuiteName[];
#endif // defined(OS_MAC)
// Install Errors. // Install Errors.
// //
// Specific install errors for the updater are reported in such a way that // Specific install errors for the updater are reported in such a way that
......
...@@ -13,11 +13,15 @@ ...@@ -13,11 +13,15 @@
#include "chrome/updater/updater_version.h" #include "chrome/updater/updater_version.h"
#include "url/gurl.h" #include "url/gurl.h"
#include "base/logging.h"
namespace updater { namespace updater {
std::vector<GURL> DevOverrideProvider::UpdateURL() const { std::vector<GURL> DevOverrideProvider::UpdateURL() const {
@autoreleasepool { @autoreleasepool {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults* userDefaults = [[NSUserDefaults alloc]
initWithSuiteName:[NSString
stringWithUTF8String:kUserDefaultsSuiteName]];
NSURL* url = [userDefaults NSURL* url = [userDefaults
URLForKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]]; URLForKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]];
if (url == nil) if (url == nil)
...@@ -28,7 +32,9 @@ std::vector<GURL> DevOverrideProvider::UpdateURL() const { ...@@ -28,7 +32,9 @@ std::vector<GURL> DevOverrideProvider::UpdateURL() const {
bool DevOverrideProvider::UseCUP() const { bool DevOverrideProvider::UseCUP() const {
@autoreleasepool { @autoreleasepool {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults* userDefaults = [[NSUserDefaults alloc]
initWithSuiteName:[NSString
stringWithUTF8String:kUserDefaultsSuiteName]];
id use_cup = [userDefaults id use_cup = [userDefaults
objectForKey:[NSString stringWithUTF8String:kDevOverrideKeyUseCUP]]; objectForKey:[NSString stringWithUTF8String:kDevOverrideKeyUseCUP]];
if (use_cup) if (use_cup)
......
...@@ -20,7 +20,9 @@ namespace { ...@@ -20,7 +20,9 @@ namespace {
void ClearUserDefaults() { void ClearUserDefaults() {
@autoreleasepool { @autoreleasepool {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults* userDefaults = [[NSUserDefaults alloc]
initWithSuiteName:[NSString
stringWithUTF8String:kUserDefaultsSuiteName]];
[userDefaults [userDefaults
removeObjectForKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]]; removeObjectForKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]];
[userDefaults [userDefaults
...@@ -43,7 +45,9 @@ TEST_F(DevOverrideTest, TestDevOverrides) { ...@@ -43,7 +45,9 @@ TEST_F(DevOverrideTest, TestDevOverrides) {
std::unique_ptr<ExternalConstants> consts = CreateExternalConstants(); std::unique_ptr<ExternalConstants> consts = CreateExternalConstants();
@autoreleasepool { @autoreleasepool {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults* userDefaults = [[NSUserDefaults alloc]
initWithSuiteName:[NSString
stringWithUTF8String:kUserDefaultsSuiteName]];
[userDefaults setURL:[NSURL URLWithString:@"http://localhost:8080"] [userDefaults setURL:[NSURL URLWithString:@"http://localhost:8080"]
forKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]]; forKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]];
[userDefaults [userDefaults
......
...@@ -37,7 +37,8 @@ namespace updater { ...@@ -37,7 +37,8 @@ namespace updater {
namespace { namespace {
constexpr char kLoggingModuleSwitchValue[] = "*/updater/*=2"; constexpr char kLoggingModuleSwitchValue[] =
"*/updater/*=2,*/update_client/*=2";
#pragma mark Helpers #pragma mark Helpers
const base::FilePath GetUpdaterAppName() { const base::FilePath GetUpdaterAppName() {
......
...@@ -10,21 +10,28 @@ ...@@ -10,21 +10,28 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/process/launch.h" #include "base/process/launch.h"
#include "base/process/process.h" #include "base/process/process.h"
#include "base/strings/strcat.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/test/task_environment.h"
#include "base/time/time.h"
#include "base/version.h" #include "base/version.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/updater/constants.h" #include "chrome/updater/constants.h"
#include "chrome/updater/persisted_data.h" #include "chrome/updater/persisted_data.h"
#include "chrome/updater/prefs.h" #include "chrome/updater/prefs.h"
#include "chrome/updater/registration_data.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/constants.h"
#include "chrome/updater/test/test_app/test_app_version.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/updater_version.h"
#include "chrome/updater/util.h" #include "chrome/updater/util.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace updater { namespace updater {
...@@ -42,6 +49,25 @@ void ExpectActiveVersion(std::string expected) { ...@@ -42,6 +49,25 @@ void ExpectActiveVersion(std::string expected) {
EXPECT_EQ(CreateGlobalPrefs()->GetActiveVersion(), 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() { void PrintLog() {
std::string contents; std::string contents;
VLOG(0) << GetDataDirPath().AppendASCII("updater.log"); VLOG(0) << GetDataDirPath().AppendASCII("updater.log");
...@@ -53,7 +79,6 @@ void PrintLog() { ...@@ -53,7 +79,6 @@ void PrintLog() {
VLOG(0) << "Failed to read updater.log file."; VLOG(0) << "Failed to read updater.log file.";
} }
} }
} // namespace
const testing::TestInfo* GetTestInfo() { const testing::TestInfo* GetTestInfo() {
return testing::UnitTest::GetInstance()->current_test_info(); return testing::UnitTest::GetInstance()->current_test_info();
...@@ -152,7 +177,7 @@ class IntegrationTest : public ::testing::Test { ...@@ -152,7 +177,7 @@ class IntegrationTest : public ::testing::Test {
void SetUp() override { void SetUp() override {
Clean(); Clean();
ExpectClean(); ExpectClean();
EnterTestMode(); EnterTestMode(GURL("http://localhost:1234"));
} }
void TearDown() override { void TearDown() override {
...@@ -200,6 +225,7 @@ TEST_F(IntegrationTest, SelfUninstallOutdatedUpdater) { ...@@ -200,6 +225,7 @@ TEST_F(IntegrationTest, SelfUninstallOutdatedUpdater) {
} }
#if defined(OS_MAC) #if defined(OS_MAC)
// TODO(crbug.com/1163524): Enable on Windows.
TEST_F(IntegrationTest, RegisterTestApp) { TEST_F(IntegrationTest, RegisterTestApp) {
RegisterTestApp(); RegisterTestApp();
ExpectInstalled(); ExpectInstalled();
...@@ -208,6 +234,45 @@ TEST_F(IntegrationTest, RegisterTestApp) { ...@@ -208,6 +234,45 @@ TEST_F(IntegrationTest, RegisterTestApp) {
Uninstall(); 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) { TEST_F(IntegrationTest, UnregisterUninstalledApp) {
RegisterTestApp(); RegisterTestApp();
ExpectInstalled(); ExpectInstalled();
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef CHROME_UPDATER_TEST_INTEGRATION_TESTS_H_ #ifndef CHROME_UPDATER_TEST_INTEGRATION_TESTS_H_
#define CHROME_UPDATER_TEST_INTEGRATION_TESTS_H_ #define CHROME_UPDATER_TEST_INTEGRATION_TESTS_H_
#include <string>
#include "build/build_config.h" #include "build/build_config.h"
namespace base { namespace base {
...@@ -13,9 +15,14 @@ class FilePath; ...@@ -13,9 +15,14 @@ class FilePath;
class Version; class Version;
} // namespace base } // namespace base
class GURL;
namespace updater { namespace updater {
namespace test { 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 // 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 // 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. // the test left the updater in an installed or partially installed state.
...@@ -26,8 +33,9 @@ void Clean(); ...@@ -26,8 +33,9 @@ void Clean();
// test. // test.
void ExpectClean(); void ExpectClean();
// Places the updater into test mode (use local servers and disable CUP). // Places the updater into test mode (use `url` as the update server and disable
void EnterTestMode(); // CUP).
void EnterTestMode(const GURL& url);
// Copies the logs to a location where they can be retrieved by ResultDB. // Copies the logs to a location where they can be retrieved by ResultDB.
void CopyLog(const base::FilePath& src_dir); void CopyLog(const base::FilePath& src_dir);
...@@ -56,7 +64,7 @@ void ExpectActive(); ...@@ -56,7 +64,7 @@ void ExpectActive();
void Uninstall(); void Uninstall();
// Runs the wake client and wait for it to exit. Assert that it exits with // 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); void RunWake(int exit_code);
// Registers the test app. As a result, the bundled updater is installed, // Registers the test app. As a result, the bundled updater is installed,
...@@ -88,6 +96,15 @@ void SetupFakeUpdaterHigherVersion(); ...@@ -88,6 +96,15 @@ void SetupFakeUpdaterHigherVersion();
// Expects that this version of updater is uninstalled from the system. // Expects that this version of updater is uninstalled from the system.
void ExpectCandidateUninstalled(); 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) #if defined(OS_WIN)
void ExpectInterfacesRegistered(); void ExpectInterfacesRegistered();
#endif #endif
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include <stdint.h> #include <stdint.h>
#include <string>
#include "base/command_line.h" #include "base/command_line.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
...@@ -25,6 +26,7 @@ ...@@ -25,6 +26,7 @@
#include "chrome/updater/updater_version.h" #include "chrome/updater/updater_version.h"
#include "chrome/updater/util.h" #include "chrome/updater/util.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace updater { namespace updater {
namespace test { namespace test {
...@@ -73,6 +75,15 @@ base::FilePath GetProductPath() { ...@@ -73,6 +75,15 @@ base::FilePath GetProductPath() {
.AppendASCII(PRODUCT_FULLNAME_STRING); .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) { void ExpectServiceAbsent(const std::string& service) {
bool success = false; bool success = false;
base::RunLoop loop; base::RunLoop loop;
...@@ -88,6 +99,25 @@ void ExpectServiceAbsent(const std::string& service) { ...@@ -88,6 +99,25 @@ void ExpectServiceAbsent(const std::string& service) {
} // namespace } // 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() { base::FilePath GetDataDirPath() {
return base::mac::GetUserLibraryPath() return base::mac::GetUserLibraryPath()
.AppendASCII("Application Support") .AppendASCII("Application Support")
...@@ -107,7 +137,9 @@ void Clean() { ...@@ -107,7 +137,9 @@ void Clean() {
EXPECT_TRUE(base::DeletePathRecursively(GetDataDirPath())); EXPECT_TRUE(base::DeletePathRecursively(GetDataDirPath()));
@autoreleasepool { @autoreleasepool {
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults* userDefaults = [[NSUserDefaults alloc]
initWithSuiteName:[NSString
stringWithUTF8String:kUserDefaultsSuiteName]];
[userDefaults [userDefaults
removeObjectForKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]]; removeObjectForKey:[NSString stringWithUTF8String:kDevOverrideKeyUrl]];
[userDefaults [userDefaults
...@@ -138,18 +170,6 @@ void ExpectClean() { ...@@ -138,18 +170,6 @@ void ExpectClean() {
ExpectServiceAbsent(kUpdateServiceInternalLaunchdName); 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() { void ExpectInstalled() {
// Files must exist on the file system. // Files must exist on the file system.
EXPECT_TRUE(base::PathExists(GetProductPath())); EXPECT_TRUE(base::PathExists(GetProductPath()));
...@@ -200,6 +220,8 @@ void ExpectCandidateUninstalled() { ...@@ -200,6 +220,8 @@ void ExpectCandidateUninstalled() {
} }
void Uninstall() { void Uninstall() {
if (::testing::Test::HasFailure())
PrintLog();
// Copy logs from GetDataDirPath() before updater uninstalls itself // Copy logs from GetDataDirPath() before updater uninstalls itself
// and deletes the path. // and deletes the path.
CopyLog(GetDataDirPath()); CopyLog(GetDataDirPath());
...@@ -218,6 +240,26 @@ base::FilePath GetFakeUpdaterInstallFolderPath(const base::Version& version) { ...@@ -218,6 +240,26 @@ base::FilePath GetFakeUpdaterInstallFolderPath(const base::Version& version) {
return GetExecutableFolderPathForVersion(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) #endif // !defined(COMPONENT_BUILD)
} // namespace test } // namespace test
......
...@@ -3,11 +3,14 @@ ...@@ -3,11 +3,14 @@
// found in the LICENSE file. // found in the LICENSE file.
#include <wrl/client.h> #include <wrl/client.h>
#include <string>
#include "base/command_line.h" #include "base/command_line.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/path_service.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/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include "base/task/task_traits.h" #include "base/task/task_traits.h"
...@@ -23,11 +26,14 @@ ...@@ -23,11 +26,14 @@
#include "chrome/updater/util.h" #include "chrome/updater/util.h"
#include "chrome/updater/win/constants.h" #include "chrome/updater/win/constants.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace updater { namespace updater {
namespace test { namespace test {
namespace { namespace {
constexpr base::char16 kDidRun[] = L"dr";
base::FilePath GetInstallerPath() { base::FilePath GetInstallerPath() {
base::FilePath test_executable; base::FilePath test_executable;
if (!base::PathService::Get(base::FILE_EXE, &test_executable)) if (!base::PathService::Get(base::FILE_EXE, &test_executable))
...@@ -44,6 +50,10 @@ base::FilePath GetProductPath() { ...@@ -44,6 +50,10 @@ base::FilePath GetProductPath() {
.AppendASCII(UPDATER_VERSION_STRING); .AppendASCII(UPDATER_VERSION_STRING);
} }
base::string16 GetAppClientStateKey(const std::string& id) {
return base::ASCIIToUTF16(base::StrCat({CLIENT_STATE_KEY, id}));
}
} // namespace } // namespace
base::FilePath GetInstalledExecutablePath() { base::FilePath GetInstalledExecutablePath() {
...@@ -88,13 +98,12 @@ void ExpectClean() { ...@@ -88,13 +98,12 @@ void ExpectClean() {
EXPECT_FALSE(base::PathExists(GetDataDirPath())); EXPECT_FALSE(base::PathExists(GetDataDirPath()));
} }
void EnterTestMode() { void EnterTestMode(const GURL& url) {
// TODO(crbug.com/1119857): Point this to an actual fake server.
base::win::RegKey key(HKEY_CURRENT_USER, L"", KEY_SET_VALUE); base::win::RegKey key(HKEY_CURRENT_USER, L"", KEY_SET_VALUE);
ASSERT_EQ(key.Create(HKEY_CURRENT_USER, UPDATE_DEV_KEY, KEY_WRITE), ASSERT_EQ(key.Create(HKEY_CURRENT_USER, UPDATE_DEV_KEY, KEY_WRITE),
ERROR_SUCCESS); ERROR_SUCCESS);
ASSERT_EQ(key.WriteValue(base::UTF8ToUTF16(kDevOverrideKeyUrl).c_str(), ASSERT_EQ(key.WriteValue(base::UTF8ToUTF16(kDevOverrideKeyUrl).c_str(),
L"http://localhost:8367"), base::UTF8ToUTF16(url.spec()).c_str()),
ERROR_SUCCESS); ERROR_SUCCESS);
ASSERT_EQ(key.WriteValue(base::UTF8ToUTF16(kDevOverrideKeyUseCUP).c_str(), ASSERT_EQ(key.WriteValue(base::UTF8ToUTF16(kDevOverrideKeyUseCUP).c_str(),
DWORD{0}), DWORD{0}),
...@@ -139,6 +148,8 @@ void Install() { ...@@ -139,6 +148,8 @@ void Install() {
} }
void Uninstall() { void Uninstall() {
if (::testing::Test::HasFailure())
PrintLog();
// Copy logs from GetDataDirPath() before updater uninstalls itself // Copy logs from GetDataDirPath() before updater uninstalls itself
// and deletes the path. // and deletes the path.
CopyLog(GetDataDirPath()); CopyLog(GetDataDirPath());
...@@ -160,6 +171,37 @@ void Uninstall() { ...@@ -160,6 +171,37 @@ void Uninstall() {
SleepFor(5); 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 // Tests if the typelibs and some of the public, internal, and
// legacy interfaces are available. Failure to query these interfaces indicates // legacy interfaces are available. Failure to query these interfaces indicates
// an issue with typelib registration. // 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