blob: 11a13c2d815fb5c7f38740ab949053f17f493305 [file] [log] [blame]
// Copyright (c) 2015 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/browser/chromeos/policy/system_log_uploader.h"
#include <utility>
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_simple_task_runner.h"
#include "base/time/time.h"
#include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/testing_browser_process.h"
#include "components/feedback/redaction_tool.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_utils.h"
#include "net/http/http_request_headers.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace policy {
namespace {
// Pseudo-location of policy dump file.
constexpr char kPolicyDumpFileLocation[] = "/var/log/policy_dump.json";
constexpr char kPolicyDump[] = "{}";
// The list of tested system log file names.
const char* const kTestSystemLogFileNames[] = {"name1.txt", "name32.txt"};
constexpr char kZippedData[] = "zipped_data";
// Generate the fake system log files.
SystemLogUploader::SystemLogs GenerateTestSystemLogFiles() {
SystemLogUploader::SystemLogs system_logs;
for (auto* file_path : kTestSystemLogFileNames) {
system_logs.push_back(std::make_pair(file_path, file_path));
}
return system_logs;
}
class MockUploadJob : public UploadJob {
public:
// If is_upload_error is false OnSuccess() will be invoked when the
// Start() method is called, otherwise OnFailure() will be invoked.
MockUploadJob(UploadJob::Delegate* delegate,
bool is_upload_error,
int max_files);
~MockUploadJob() override;
// policy::UploadJob:
void AddDataSegment(const std::string& name,
const std::string& filename,
const std::map<std::string, std::string>& header_entries,
std::unique_ptr<std::string> data) override;
void Start() override;
protected:
UploadJob::Delegate* delegate_;
bool is_upload_error_;
int file_index_;
int max_files_;
};
MockUploadJob::MockUploadJob(UploadJob::Delegate* delegate,
bool is_upload_error,
int max_files)
: delegate_(delegate),
is_upload_error_(is_upload_error),
file_index_(0),
max_files_(max_files) {}
MockUploadJob::~MockUploadJob() {}
void MockUploadJob::AddDataSegment(
const std::string& name,
const std::string& filename,
const std::map<std::string, std::string>& header_entries,
std::unique_ptr<std::string> data) {
// Test all fields to upload.
EXPECT_LT(file_index_, max_files_);
EXPECT_GE(file_index_, 0);
EXPECT_EQ(base::StringPrintf(SystemLogUploader::kNameFieldTemplate,
file_index_ + 1),
name);
if (file_index_ == max_files_ - 1) {
EXPECT_EQ(kPolicyDumpFileLocation, filename);
} else {
EXPECT_EQ(kTestSystemLogFileNames[file_index_], filename);
}
EXPECT_EQ(2U, header_entries.size());
EXPECT_EQ(
SystemLogUploader::kFileTypeLogFile,
header_entries.find(SystemLogUploader::kFileTypeHeaderName)->second);
EXPECT_EQ(SystemLogUploader::kContentTypePlainText,
header_entries.find(net::HttpRequestHeaders::kContentType)->second);
if (file_index_ == max_files_ - 1) {
EXPECT_EQ(kPolicyDump, *data);
} else {
EXPECT_EQ(kTestSystemLogFileNames[file_index_], *data);
}
file_index_++;
}
void MockUploadJob::Start() {
DCHECK(delegate_);
// Check if all files were uploaded.
EXPECT_EQ(max_files_, file_index_);
if (is_upload_error_) {
// Send any ErrorCode.
delegate_->OnFailure(UploadJob::ErrorCode::NETWORK_ERROR);
} else {
delegate_->OnSuccess();
}
}
class MockZippedUploadJob : public MockUploadJob {
public:
// If is_upload_error is false OnSuccess() will be invoked when the
// Start() method is called, otherwise OnFailure() will be invoked.
MockZippedUploadJob(UploadJob::Delegate* delegate, bool is_upload_error);
~MockZippedUploadJob() override;
// policy::UploadJob:
void AddDataSegment(const std::string& name,
const std::string& filename,
const std::map<std::string, std::string>& header_entries,
std::unique_ptr<std::string> data) override;
};
MockZippedUploadJob::MockZippedUploadJob(UploadJob::Delegate* delegate,
bool is_upload_error)
: MockUploadJob(delegate, is_upload_error, /*max_files=*/1) {}
MockZippedUploadJob::~MockZippedUploadJob() {}
void MockZippedUploadJob::AddDataSegment(
const std::string& name,
const std::string& filename,
const std::map<std::string, std::string>& header_entries,
std::unique_ptr<std::string> data) {
// Test all fields to upload.
EXPECT_LT(file_index_, max_files_);
EXPECT_GE(file_index_, 0);
EXPECT_EQ(SystemLogUploader::kZippedLogsName, name);
EXPECT_EQ(SystemLogUploader::kZippedLogsFileName, filename);
EXPECT_EQ(2U, header_entries.size());
EXPECT_EQ(
SystemLogUploader::kFileTypeZippedLogFile,
header_entries.find(SystemLogUploader::kFileTypeHeaderName)->second);
EXPECT_EQ(SystemLogUploader::kContentTypeOctetStream,
header_entries.find(net::HttpRequestHeaders::kContentType)->second);
EXPECT_EQ(kZippedData, *data);
file_index_++;
}
// MockSystemLogDelegate - mock class that creates an upload job and runs upload
// callback.
class MockSystemLogDelegate : public SystemLogUploader::Delegate {
public:
MockSystemLogDelegate(bool is_upload_error,
const SystemLogUploader::SystemLogs& system_logs,
bool is_zipped_upload)
: is_upload_error_(is_upload_error),
system_logs_(system_logs),
is_zipped_upload_(is_zipped_upload) {}
~MockSystemLogDelegate() override {}
std::string GetPolicyAsJSON() override { return kPolicyDump; }
void LoadSystemLogs(LogUploadCallback upload_callback) override {
EXPECT_TRUE(is_upload_allowed_);
std::move(upload_callback)
.Run(std::make_unique<SystemLogUploader::SystemLogs>(system_logs_));
}
std::unique_ptr<UploadJob> CreateUploadJob(
const GURL& url,
UploadJob::Delegate* delegate) override {
if (is_zipped_upload_)
return std::make_unique<MockZippedUploadJob>(delegate, is_upload_error_);
return std::make_unique<MockUploadJob>(delegate, is_upload_error_,
system_logs_.size() + 1);
}
void ZipSystemLogs(std::unique_ptr<SystemLogUploader::SystemLogs> system_logs,
ZippedLogUploadCallback upload_callback) override {
EXPECT_TRUE(is_zipped_upload_);
for (const auto& log : system_logs_)
EXPECT_NE(system_logs->end(),
std::find(system_logs->begin(), system_logs->end(), log));
std::move(upload_callback).Run(std::string(kZippedData));
}
void set_upload_allowed(bool is_upload_allowed) {
is_upload_allowed_ = is_upload_allowed;
}
private:
bool is_upload_allowed_;
bool is_upload_error_;
SystemLogUploader::SystemLogs system_logs_;
bool is_zipped_upload_;
};
} // namespace
class SystemLogUploaderTest : public testing::TestWithParam<bool> {
public:
TestingPrefServiceSimple local_state_;
SystemLogUploaderTest()
: task_runner_(new base::TestSimpleTaskRunner()),
is_zipped_upload_(GetParam()) {
feature_list.InitWithFeatureState(features::kUploadZippedSystemLogs,
is_zipped_upload_);
}
void SetUp() override {
RegisterLocalState(local_state_.registry());
TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
settings_helper_.ReplaceDeviceSettingsProviderWithStub();
}
void TearDown() override {
TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
settings_helper_.RestoreRealDeviceSettingsProvider();
content::RunAllTasksUntilIdle();
}
// Given a pending task to upload system logs.
void RunPendingUploadTaskAndCheckNext(const SystemLogUploader& uploader,
base::TimeDelta expected_delay) {
EXPECT_TRUE(task_runner_->HasPendingTask());
task_runner_->RunPendingTasks();
// The previous task should have uploaded another log upload task.
EXPECT_EQ(1U, task_runner_->NumPendingTasks());
CheckPendingTaskDelay(uploader, expected_delay);
}
void CheckPendingTaskDelay(const SystemLogUploader& uploader,
base::TimeDelta expected_delay) {
// The next task should be scheduled sometime between
// |last_upload_attempt| + |expected_delay| and
// |now| + |expected_delay|.
base::Time now = base::Time::NowFromSystemTime();
base::Time next_task = now + task_runner_->NextPendingTaskDelay();
EXPECT_LE(next_task, now + expected_delay);
EXPECT_GE(next_task, uploader.last_upload_attempt() + expected_delay);
}
void ExpectSuccessHistogram(int amount) {
histogram_tester_.ExpectUniqueSample(
SystemLogUploader::kSystemLogUploadResultHistogram,
is_zipped_upload_ ? SystemLogUploader::ZIPPED_LOGS_UPLOAD_SUCCESS
: SystemLogUploader::NON_ZIPPED_LOGS_UPLOAD_SUCCESS,
amount);
}
void ExpectFailureHistogram(int amount) {
histogram_tester_.ExpectUniqueSample(
SystemLogUploader::kSystemLogUploadResultHistogram,
is_zipped_upload_ ? SystemLogUploader::ZIPPED_LOGS_UPLOAD_FAILURE
: SystemLogUploader::NON_ZIPPED_LOGS_UPLOAD_FAILURE,
amount);
}
protected:
content::BrowserTaskEnvironment task_environment_;
ash::ScopedCrosSettingsTestHelper settings_helper_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
bool is_zipped_upload_;
base::test::ScopedFeatureList feature_list;
base::HistogramTester histogram_tester_;
};
// Verify log throttling. Try successive kLogThrottleCount log uploads by
// creating a new task. First kLogThrottleCount logs should have 0 delay.
// Successive logs should have delay greater than zero.
TEST_P(SystemLogUploaderTest, LogThrottleTest) {
for (int upload_num = 0;
upload_num < SystemLogUploader::kLogThrottleCount + 3; upload_num++) {
EXPECT_FALSE(task_runner_->HasPendingTask());
auto syslog_delegate = std::make_unique<MockSystemLogDelegate>(
false, SystemLogUploader::SystemLogs(), is_zipped_upload_);
syslog_delegate->set_upload_allowed(true);
settings_helper_.SetBoolean(chromeos::kSystemLogUploadEnabled, true);
SystemLogUploader uploader(std::move(syslog_delegate), task_runner_);
EXPECT_EQ(1U, task_runner_->NumPendingTasks());
if (upload_num < SystemLogUploader::kLogThrottleCount) {
EXPECT_EQ(task_runner_->NextPendingTaskDelay(),
base::TimeDelta::FromMilliseconds(0));
} else {
EXPECT_GT(task_runner_->NextPendingTaskDelay(),
base::TimeDelta::FromMilliseconds(0));
}
task_runner_->RunPendingTasks();
task_runner_->ClearPendingTasks();
}
}
// Verify that we never throttle immediate log upload.
TEST_P(SystemLogUploaderTest, ImmediateLogUpload) {
EXPECT_FALSE(task_runner_->HasPendingTask());
auto syslog_delegate = std::make_unique<MockSystemLogDelegate>(
false, SystemLogUploader::SystemLogs(), is_zipped_upload_);
syslog_delegate->set_upload_allowed(true);
settings_helper_.SetBoolean(chromeos::kSystemLogUploadEnabled, true);
SystemLogUploader uploader(std::move(syslog_delegate), task_runner_);
for (int upload_num = 0;
upload_num < SystemLogUploader::kLogThrottleCount + 3; upload_num++) {
uploader.ScheduleNextSystemLogUploadImmediately();
EXPECT_EQ(task_runner_->NextPendingTaskDelay(),
base::TimeDelta::FromMilliseconds(0));
task_runner_->RunPendingTasks();
task_runner_->ClearPendingTasks();
}
}
// Check disabled system log uploads by default.
TEST_P(SystemLogUploaderTest, Basic) {
EXPECT_FALSE(task_runner_->HasPendingTask());
std::unique_ptr<MockSystemLogDelegate> syslog_delegate(
new MockSystemLogDelegate(/*is_upload_error=*/false,
SystemLogUploader::SystemLogs(),
is_zipped_upload_));
syslog_delegate->set_upload_allowed(false);
SystemLogUploader uploader(std::move(syslog_delegate), task_runner_);
task_runner_->RunPendingTasks();
histogram_tester_.ExpectTotalCount(
SystemLogUploader::kSystemLogUploadResultHistogram, 0);
}
// One success task pending.
TEST_P(SystemLogUploaderTest, SuccessTest) {
EXPECT_FALSE(task_runner_->HasPendingTask());
std::unique_ptr<MockSystemLogDelegate> syslog_delegate(
new MockSystemLogDelegate(/*is_upload_error=*/false,
SystemLogUploader::SystemLogs(),
is_zipped_upload_));
syslog_delegate->set_upload_allowed(true);
settings_helper_.SetBoolean(chromeos::kSystemLogUploadEnabled, true);
SystemLogUploader uploader(std::move(syslog_delegate), task_runner_);
EXPECT_EQ(1U, task_runner_->NumPendingTasks());
RunPendingUploadTaskAndCheckNext(
uploader, base::TimeDelta::FromMilliseconds(
SystemLogUploader::kDefaultUploadDelayMs));
ExpectSuccessHistogram(/*amount=*/1);
}
// Three failed responses received.
TEST_P(SystemLogUploaderTest, ThreeFailureTest) {
EXPECT_FALSE(task_runner_->HasPendingTask());
std::unique_ptr<MockSystemLogDelegate> syslog_delegate(
new MockSystemLogDelegate(/*is_upload_error=*/true,
SystemLogUploader::SystemLogs(),
is_zipped_upload_));
syslog_delegate->set_upload_allowed(true);
settings_helper_.SetBoolean(chromeos::kSystemLogUploadEnabled, true);
SystemLogUploader uploader(std::move(syslog_delegate), task_runner_);
EXPECT_EQ(1U, task_runner_->NumPendingTasks());
// Do not retry two times consequentially.
RunPendingUploadTaskAndCheckNext(uploader,
base::TimeDelta::FromMilliseconds(
SystemLogUploader::kErrorUploadDelayMs));
// We are using the kDefaultUploadDelayMs and not the kErrorUploadDelayMs here
// because there's just one retry.
RunPendingUploadTaskAndCheckNext(
uploader, base::TimeDelta::FromMilliseconds(
SystemLogUploader::kDefaultUploadDelayMs));
RunPendingUploadTaskAndCheckNext(uploader,
base::TimeDelta::FromMilliseconds(
SystemLogUploader::kErrorUploadDelayMs));
ExpectFailureHistogram(/*amount=*/3);
}
// Check header fields of system log files to upload.
TEST_P(SystemLogUploaderTest, CheckHeaders) {
EXPECT_FALSE(task_runner_->HasPendingTask());
SystemLogUploader::SystemLogs system_logs = GenerateTestSystemLogFiles();
std::unique_ptr<MockSystemLogDelegate> syslog_delegate(
new MockSystemLogDelegate(/*is_upload_error=*/false, system_logs,
is_zipped_upload_));
syslog_delegate->set_upload_allowed(true);
settings_helper_.SetBoolean(chromeos::kSystemLogUploadEnabled, true);
SystemLogUploader uploader(std::move(syslog_delegate), task_runner_);
EXPECT_EQ(1U, task_runner_->NumPendingTasks());
RunPendingUploadTaskAndCheckNext(
uploader, base::TimeDelta::FromMilliseconds(
SystemLogUploader::kDefaultUploadDelayMs));
ExpectSuccessHistogram(/*amount=*/1);
}
// Disable system log uploads after one failed log upload.
TEST_P(SystemLogUploaderTest, DisableLogUpload) {
EXPECT_FALSE(task_runner_->HasPendingTask());
std::unique_ptr<MockSystemLogDelegate> syslog_delegate(
new MockSystemLogDelegate(/*is_upload_error=*/true,
SystemLogUploader::SystemLogs(),
is_zipped_upload_));
MockSystemLogDelegate* mock_delegate = syslog_delegate.get();
settings_helper_.SetBoolean(chromeos::kSystemLogUploadEnabled, true);
mock_delegate->set_upload_allowed(true);
SystemLogUploader uploader(std::move(syslog_delegate), task_runner_);
EXPECT_EQ(1U, task_runner_->NumPendingTasks());
RunPendingUploadTaskAndCheckNext(uploader,
base::TimeDelta::FromMilliseconds(
SystemLogUploader::kErrorUploadDelayMs));
ExpectFailureHistogram(/*amount=*/1);
// Disable log upload and check that frequency is usual, because there is no
// errors, we should not upload logs.
settings_helper_.SetBoolean(chromeos::kSystemLogUploadEnabled, false);
mock_delegate->set_upload_allowed(false);
task_runner_->RunPendingTasks();
RunPendingUploadTaskAndCheckNext(
uploader, base::TimeDelta::FromMilliseconds(
SystemLogUploader::kDefaultUploadDelayMs));
RunPendingUploadTaskAndCheckNext(
uploader, base::TimeDelta::FromMilliseconds(
SystemLogUploader::kDefaultUploadDelayMs));
ExpectFailureHistogram(/*amount=*/1);
}
INSTANTIATE_TEST_SUITE_P(SystemLogUploaderTestInstance,
SystemLogUploaderTest,
testing::Bool());
} // namespace policy