#include "net/log/file_net_log_observer.h"
#include <string>
#include <vector>
#include "base/containers/span.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/gmock_expected_support.h"
#include "base/threading/thread.h"
#include "base/types/expected.h"
#include "base/types/expected_macros.h"
#include "base/values.h"
#include "build/build_config.h"
#include "net/base/test_completion_callback.h"
#include "net/log/net_log.h"
#include "net/log/net_log_entry.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_source.h"
#include "net/log/net_log_source_type.h"
#include "net/log/net_log_util.h"
#include "net/log/net_log_values.h"
#include "net/test/test_with_task_environment.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
const int kTotalNumFiles = 10;
const int kLargeFileSize = 100000000;
const size_t kDummyEventSize = 150;
void AddEntries(FileNetLogObserver* logger,
int num_entries,
size_t entry_size) {
const int kDummyId = 0;
NetLogSource source(NetLogSourceType::HTTP2_SESSION, kDummyId);
NetLogEntry base_entry(NetLogEventType::PAC_JAVASCRIPT_ERROR, source,
NetLogEventPhase::BEGIN, base::TimeTicks::Now(),
NetLogParamsWithString("message", ""));
std::string json = base::WriteJson(base_entry.ToDict()).value_or("");
size_t base_entry_size = json.size();
DCHECK_LE(base_entry_size, 136u);
EXPECT_GE(entry_size, 136u);
EXPECT_GE(entry_size, base_entry_size);
for (int i = 0; i < num_entries; i++) {
source = NetLogSource(NetLogSourceType::HTTP2_SESSION, i);
std::string id = base::NumberToString(i);
std::string message =
std::string(entry_size - base_entry_size - id.size() + 1, 'x');
NetLogEntry entry(NetLogEventType::PAC_JAVASCRIPT_ERROR, source,
NetLogEventPhase::BEGIN, base::TimeTicks::Now(),
NetLogParamsWithString("message", message));
logger->OnAddEntry(entry);
}
}
struct ParsedNetLog {
base::expected<void, std::string> InitFromFileContents(
const std::string& input);
const base::Value::Dict* GetEvent(size_t i) const;
base::Value root;
raw_ptr<const base::Value::Dict> constants = nullptr;
raw_ptr<const base::Value::List> events = nullptr;
raw_ptr<const base::Value::Dict> polled_data = nullptr;
};
base::expected<void, std::string> ParsedNetLog::InitFromFileContents(
const std::string& input) {
if (input.empty()) {
return base::unexpected("input is empty");
}
ASSIGN_OR_RETURN(root,
base::JSONReader::ReadAndReturnValueWithError(
input, base::JSON_PARSE_CHROMIUM_EXTENSIONS),
&base::JSONReader::Error::message);
const base::Value::Dict* dict = root.GetIfDict();
if (!dict) {
return base::unexpected("Not a dictionary");
}
events = dict->FindListByDottedPath("events");
if (!events) {
return base::unexpected("No events list");
}
constants = dict->FindDictByDottedPath("constants");
if (!constants) {
return base::unexpected("No constants dictionary");
}
polled_data = dict->FindDictByDottedPath("polledData");
return base::ok();
}
const base::Value::Dict* ParsedNetLog::GetEvent(size_t i) const {
if (!events || i >= events->size())
return nullptr;
return (*events)[i].GetIfDict();
}
base::expected<std::unique_ptr<ParsedNetLog>, std::string> ReadNetLogFromDisk(
const base::FilePath& log_path) {
std::string input;
if (!base::ReadFileToString(log_path, &input)) {
return base::unexpected("Failed reading file: " +
base::UTF16ToUTF8(log_path.LossyDisplayName()));
}
std::unique_ptr<ParsedNetLog> result = std::make_unique<ParsedNetLog>();
RETURN_IF_ERROR(result->InitFromFileContents(input));
return result;
}
void VerifyEventsInLog(const ParsedNetLog* log,
size_t num_events_emitted,
size_t num_events_saved) {
ASSERT_TRUE(log);
ASSERT_LE(num_events_saved, num_events_emitted);
ASSERT_EQ(num_events_saved, log->events->size());
for (size_t i = 0; i < num_events_saved; ++i) {
const base::Value::Dict* event = log->GetEvent(i);
ASSERT_TRUE(event);
size_t expected_source_id = num_events_emitted - num_events_saved + i;
std::optional<int> id_value = event->FindIntByDottedPath("source.id");
ASSERT_EQ(static_cast<int>(expected_source_id), id_value);
}
}
void ExpectDictionaryContainsProperty(const base::Value::Dict& dict,
const std::string& key,
const std::string& value) {
const std::string* actual_value = dict.FindStringByDottedPath(key);
ASSERT_EQ(value, *actual_value);
}
class FileNetLogObserverTest : public ::testing::TestWithParam<bool>,
public WithTaskEnvironment {
public:
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
log_path_ = temp_dir_.GetPath().AppendASCII("net-log.json");
}
void TearDown() override {
logger_.reset();
RunUntilIdle();
}
bool IsBounded() const { return GetParam(); }
void CreateAndStartObserving(
std::unique_ptr<base::Value::Dict> constants,
NetLogCaptureMode capture_mode = NetLogCaptureMode::kDefault) {
if (IsBounded()) {
logger_ = FileNetLogObserver::CreateBoundedForTests(
log_path_, kLargeFileSize, kTotalNumFiles, capture_mode,
std::move(constants));
} else {
logger_ = FileNetLogObserver::CreateUnbounded(log_path_, capture_mode,
std::move(constants));
}
logger_->StartObserving(NetLog::Get());
}
void CreateAndStartObservingBoundedFile(
int max_file_size,
std::unique_ptr<base::Value::Dict> constants) {
base::File file(log_path_,
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
EXPECT_TRUE(file.IsValid());
file.WriteAtCurrentPos(base::as_byte_span("not json"));
logger_ = FileNetLogObserver::CreateBoundedFile(
std::move(file), max_file_size, NetLogCaptureMode::kDefault,
std::move(constants));
logger_->StartObserving(NetLog::Get());
}
void CreateAndStartObservingPreExisting(
std::unique_ptr<base::Value::Dict> constants) {
ASSERT_TRUE(scratch_dir_.CreateUniqueTempDir());
base::File file(log_path_,
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
EXPECT_TRUE(file.IsValid());
file.WriteAtCurrentPos(base::as_byte_span("not json"));
if (IsBounded()) {
logger_ = FileNetLogObserver::CreateBoundedPreExisting(
scratch_dir_.GetPath(), std::move(file), kLargeFileSize,
NetLogCaptureMode::kDefault, std::move(constants));
} else {
logger_ = FileNetLogObserver::CreateUnboundedPreExisting(
std::move(file), NetLogCaptureMode::kDefault, std::move(constants));
}
logger_->StartObserving(NetLog::Get());
}
bool LogFileExists() {
base::ThreadPoolInstance::Get()->FlushForTesting();
return base::PathExists(log_path_);
}
protected:
std::unique_ptr<FileNetLogObserver> logger_;
base::ScopedTempDir temp_dir_;
base::ScopedTempDir scratch_dir_;
base::FilePath log_path_;
};
class FileNetLogObserverBoundedTest : public ::testing::Test,
public WithTaskEnvironment {
public:
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
log_path_ = temp_dir_.GetPath().AppendASCII("net-log.json");
}
void TearDown() override {
logger_.reset();
RunUntilIdle();
}
void CreateAndStartObserving(std::unique_ptr<base::Value::Dict> constants,
uint64_t total_file_size,
int num_files) {
logger_ = FileNetLogObserver::CreateBoundedForTests(
log_path_, total_file_size, num_files, NetLogCaptureMode::kDefault,
std::move(constants));
logger_->StartObserving(NetLog::Get());
}
base::FilePath GetInprogressDirectory() const {
return log_path_.AddExtension(FILE_PATH_LITERAL(".inprogress"));
}
base::FilePath GetEventFilePath(int index) const {
return GetInprogressDirectory().AppendASCII(
"event_file_" + base::NumberToString(index) + ".json");
}
base::FilePath GetEndNetlogPath() const {
return GetInprogressDirectory().AppendASCII("end_netlog.json");
}
base::FilePath GetConstantsPath() const {
return GetInprogressDirectory().AppendASCII("constants.json");
}
protected:
std::unique_ptr<FileNetLogObserver> logger_;
base::FilePath log_path_;
private:
base::ScopedTempDir temp_dir_;
};
INSTANTIATE_TEST_SUITE_P(All,
FileNetLogObserverTest,
::testing::Values(true, false));
TEST_P(FileNetLogObserverTest, ObserverDestroyedWithoutStopObserving) {
CreateAndStartObserving(nullptr);
AddEntries(logger_.get(), 1, kDummyEventSize);
ASSERT_TRUE(LogFileExists());
logger_.reset();
ASSERT_FALSE(LogFileExists());
}
TEST_P(FileNetLogObserverTest,
ObserverDestroyedWithoutStopObservingPreExisting) {
CreateAndStartObservingPreExisting(nullptr);
AddEntries(logger_.get(), 1, kDummyEventSize);
ASSERT_TRUE(LogFileExists());
if (IsBounded()) {
ASSERT_TRUE(base::PathExists(scratch_dir_.GetPath()));
}
logger_.reset();
ASSERT_TRUE(LogFileExists());
if (IsBounded()) {
ASSERT_FALSE(base::PathExists(scratch_dir_.GetPath()));
}
}
TEST_P(FileNetLogObserverTest, StopObservingNullClosure) {
CreateAndStartObserving(nullptr);
AddEntries(logger_.get(), 1, kDummyEventSize);
ASSERT_TRUE(LogFileExists());
logger_->StopObserving(nullptr, base::OnceClosure());
logger_.reset();
ASSERT_TRUE(LogFileExists());
}
TEST_P(FileNetLogObserverTest, InitLogWithInvalidPath) {
log_path_ = temp_dir_.GetPath().AppendASCII("bogus").AppendASCII("path");
CreateAndStartObserving(nullptr);
AddEntries(logger_.get(), 1, kDummyEventSize);
ASSERT_FALSE(LogFileExists());
logger_->StopObserving(nullptr, base::OnceClosure());
logger_.reset();
ASSERT_FALSE(LogFileExists());
}
TEST_P(FileNetLogObserverTest, GeneratesValidJSONWithNoEvents) {
TestClosure closure;
CreateAndStartObserving(nullptr);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
ASSERT_EQ(0u, log->events->size());
}
TEST_P(FileNetLogObserverTest, GeneratesValidJSONWithOneEvent) {
TestClosure closure;
CreateAndStartObserving(nullptr);
AddEntries(logger_.get(), 1, kDummyEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
ASSERT_EQ(1u, log->events->size());
}
TEST_P(FileNetLogObserverTest, GeneratesValidJSONWithOneEventPreExisting) {
TestClosure closure;
CreateAndStartObservingPreExisting(nullptr);
AddEntries(logger_.get(), 1, kDummyEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
ASSERT_EQ(1u, log->events->size());
}
TEST_P(FileNetLogObserverTest,
GeneratesValidJSONWithNoEventsCreateBoundedFile) {
TestClosure closure;
CreateAndStartObservingBoundedFile(kLargeFileSize, nullptr);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
ASSERT_EQ(0u, log->events->size());
}
TEST_P(FileNetLogObserverTest,
GeneratesValidJSONWithOneEventCreateBoundedFile) {
TestClosure closure;
CreateAndStartObservingBoundedFile(kLargeFileSize, nullptr);
AddEntries(logger_.get(), 1, kDummyEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
ASSERT_EQ(1u, log->events->size());
}
TEST_P(FileNetLogObserverTest, BoundedFileFillsFile) {
const int kTotalFileSize = 10000;
const int kEventSize = 200;
const int kFileSize = kTotalFileSize;
const int kNumEvents = kFileSize / kEventSize;
TestClosure closure;
CreateAndStartObservingBoundedFile(kTotalFileSize, nullptr);
AddEntries(logger_.get(), kNumEvents, kEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
}
TEST_P(FileNetLogObserverTest, BoundedFileTruncatesEventsAfterLimit) {
const int kTotalFileSize = 10000;
const int kEventSize = 200;
const int kFileSize = kTotalFileSize;
const int kNumEvents = kFileSize / kEventSize;
TestClosure closure;
CreateAndStartObservingBoundedFile(kTotalFileSize, nullptr);
AddEntries(logger_.get(), kNumEvents * 2, kEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
}
TEST_P(FileNetLogObserverTest, PreExistingFileBroken) {
ASSERT_TRUE(scratch_dir_.CreateUniqueTempDir());
base::File file;
EXPECT_FALSE(file.IsValid());
if (IsBounded())
logger_ = FileNetLogObserver::CreateBoundedPreExisting(
scratch_dir_.GetPath(), std::move(file), kLargeFileSize,
NetLogCaptureMode::kDefault, nullptr);
else
logger_ = FileNetLogObserver::CreateUnboundedPreExisting(
std::move(file), NetLogCaptureMode::kDefault, nullptr);
logger_->StartObserving(NetLog::Get());
AddEntries(logger_.get(), 1, kDummyEventSize);
TestClosure closure;
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
}
TEST_P(FileNetLogObserverTest, CustomConstants) {
TestClosure closure;
const char kConstantKey[] = "magic";
const char kConstantString[] = "poney";
base::Value::Dict constants;
constants.SetByDottedPath(kConstantKey, kConstantString);
CreateAndStartObserving(
std::make_unique<base::Value::Dict>(std::move(constants)));
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
ExpectDictionaryContainsProperty(*log->constants, kConstantKey,
kConstantString);
}
TEST_P(FileNetLogObserverTest, GeneratesValidJSONWithPolledData) {
TestClosure closure;
CreateAndStartObserving(nullptr);
const char kDummyPolledDataPath[] = "dummy_path";
const char kDummyPolledDataString[] = "dummy_info";
base::Value::Dict dummy_polled_data;
dummy_polled_data.SetByDottedPath(kDummyPolledDataPath,
kDummyPolledDataString);
logger_->StopObserving(
std::make_unique<base::Value>(std::move(dummy_polled_data)),
closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
ASSERT_EQ(0u, log->events->size());
ASSERT_TRUE(log->polled_data);
ExpectDictionaryContainsProperty(*log->polled_data, kDummyPolledDataPath,
kDummyPolledDataString);
}
TEST_P(FileNetLogObserverTest, LogModeRecorded) {
struct TestCase {
NetLogCaptureMode capture_mode;
const char* expected_value;
} test_cases[] = {
{NetLogCaptureMode::kEverything, "Everything"},
{NetLogCaptureMode::kIncludeSensitive, "IncludeSensitive"},
{NetLogCaptureMode::kDefault, "Default"}};
TestClosure closure;
for (const auto& test_case : test_cases) {
CreateAndStartObserving(nullptr, test_case.capture_mode);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
ExpectDictionaryContainsProperty(*log->constants, "logCaptureMode",
test_case.expected_value);
}
}
TEST_P(FileNetLogObserverTest, AddEventsFromMultipleThreads) {
const size_t kNumThreads = 10;
std::vector<std::unique_ptr<base::Thread>> threads(kNumThreads);
#if BUILDFLAG(IS_FUCHSIA)
LOG(ERROR) << "Create and start threads.";
#endif
for (size_t i = 0; i < threads.size(); ++i) {
threads[i] = std::make_unique<base::Thread>("WorkerThread" +
base::NumberToString(i));
threads[i]->Start();
threads[i]->WaitUntilThreadStarted();
}
#if BUILDFLAG(IS_FUCHSIA)
LOG(ERROR) << "Create and start observing.";
#endif
CreateAndStartObserving(nullptr);
const size_t kNumEventsAddedPerThread = 200;
#if BUILDFLAG(IS_FUCHSIA)
LOG(ERROR) << "Posting tasks.";
#endif
for (size_t i = 0; i < kNumThreads; ++i) {
threads[i]->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&AddEntries, base::Unretained(logger_.get()),
kNumEventsAddedPerThread, kDummyEventSize));
}
#if BUILDFLAG(IS_FUCHSIA)
LOG(ERROR) << "Joining all threads.";
#endif
threads.clear();
#if BUILDFLAG(IS_FUCHSIA)
LOG(ERROR) << "Stop observing.";
#endif
TestClosure closure;
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
#if BUILDFLAG(IS_FUCHSIA)
LOG(ERROR) << "Read log from disk and verify.";
#endif
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
EXPECT_EQ(kNumEventsAddedPerThread * kNumThreads, log->events->size());
#if BUILDFLAG(IS_FUCHSIA)
LOG(ERROR) << "Teardown.";
#endif
}
TEST_F(FileNetLogObserverBoundedTest, EqualToOneFile) {
const int kTotalFileSize = 5000;
const int kNumEvents = 2;
const int kEventSize = 250;
TestClosure closure;
CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
AddEntries(logger_.get(), kNumEvents, kEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
}
TEST_F(FileNetLogObserverBoundedTest, OneEventOverOneFile) {
const int kTotalFileSize = 6000;
const int kNumEvents = 4;
const int kEventSize = 200;
TestClosure closure;
CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
AddEntries(logger_.get(), kNumEvents, kEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
}
TEST_F(FileNetLogObserverBoundedTest, EqualToTwoFiles) {
const int kTotalFileSize = 6000;
const int kNumEvents = 6;
const int kEventSize = 200;
TestClosure closure;
CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
AddEntries(logger_.get(), kNumEvents, kEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
}
TEST_F(FileNetLogObserverBoundedTest, FillAllFilesNoOverwriting) {
const int kTotalFileSize = 10000;
const int kEventSize = 200;
const int kFileSize = kTotalFileSize / kTotalNumFiles;
const int kNumEvents = kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1);
TestClosure closure;
CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
AddEntries(logger_.get(), kNumEvents, kEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
}
TEST_F(FileNetLogObserverBoundedTest, DropOldEventsFromWriteQueue) {
const int kTotalFileSize = 1000;
const int kNumEvents = 11;
const int kEventSize = 200;
const int kFileSize = kTotalFileSize / kTotalNumFiles;
TestClosure closure;
CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
AddEntries(logger_.get(), kNumEvents, kEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
VerifyEventsInLog(
log.get(), kNumEvents,
static_cast<size_t>(kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1)));
}
TEST_F(FileNetLogObserverBoundedTest, OverwriteAllFiles) {
const int kTotalFileSize = 6000;
const int kNumEvents = 60;
const int kEventSize = 200;
const int kFileSize = kTotalFileSize / kTotalNumFiles;
TestClosure closure;
CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
AddEntries(logger_.get(), kNumEvents, kEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
int events_per_file = (kFileSize - 1) / kEventSize + 1;
int events_in_last_file = (kNumEvents - 1) % events_per_file + 1;
int num_events_in_files =
(kTotalNumFiles - 1) * events_per_file + events_in_last_file;
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
VerifyEventsInLog(log.get(), kNumEvents,
static_cast<size_t>(num_events_in_files));
}
TEST_F(FileNetLogObserverBoundedTest, PartiallyOverwriteFiles) {
const int kTotalFileSize = 6000;
const int kNumEvents = 50;
const int kEventSize = 200;
const int kFileSize = kTotalFileSize / kTotalNumFiles;
TestClosure closure;
CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
AddEntries(logger_.get(), kNumEvents, kEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
int events_per_file = (kFileSize - 1) / kEventSize + 1;
int events_in_last_file = kNumEvents % events_per_file;
if (!events_in_last_file)
events_in_last_file = events_per_file;
int num_events_in_files =
(kTotalNumFiles - 1) * events_per_file + events_in_last_file;
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
VerifyEventsInLog(log.get(), kNumEvents,
static_cast<size_t>(num_events_in_files));
}
TEST_F(FileNetLogObserverBoundedTest, SomeFilesFailToOpen) {
const int kTotalFileSize = 10000;
const int kEventSize = 200;
const int kFileSize = kTotalFileSize / kTotalNumFiles;
const int kNumEvents = kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1);
TestClosure closure;
EXPECT_TRUE(base::CreateDirectory(GetEventFilePath(0)));
EXPECT_TRUE(base::CreateDirectory(GetEndNetlogPath()));
CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
AddEntries(logger_.get(), kNumEvents, kEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
std::string log_contents;
ASSERT_TRUE(base::ReadFileToString(log_path_, &log_contents));
EXPECT_FALSE(base::PathExists(GetInprogressDirectory()));
}
TEST_F(FileNetLogObserverBoundedTest, InprogressDirectoryBlocked) {
const int kTotalFileSize = 10000;
const int kEventSize = 200;
const int kFileSize = kTotalFileSize / kTotalNumFiles;
const int kNumEvents = kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1);
TestClosure closure;
EXPECT_TRUE(base::WriteFile(GetInprogressDirectory(), "x"));
CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
AddEntries(logger_.get(), kNumEvents, kEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
std::string log_contents;
ASSERT_TRUE(base::ReadFileToString(log_path_, &log_contents));
EXPECT_EQ("", log_contents);
EXPECT_FALSE(base::PathExists(GetInprogressDirectory()));
}
TEST_F(FileNetLogObserverBoundedTest, BlockEventsFile0) {
const int kTotalFileSize = 10000;
const int kEventSize = 200;
const int kFileSize = kTotalFileSize / kTotalNumFiles;
const int kNumEvents = kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1);
TestClosure closure;
EXPECT_TRUE(base::CreateDirectory(GetEventFilePath(0)));
CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
AddEntries(logger_.get(), kNumEvents, kEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
ASSERT_EQ(0u, log->events->size());
}
TEST_F(FileNetLogObserverBoundedTest, PreExistingUsesSpecifiedDir) {
base::ScopedTempDir scratch_dir;
ASSERT_TRUE(scratch_dir.CreateUniqueTempDir());
base::File file(log_path_, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
ASSERT_TRUE(file.IsValid());
file.WriteAtCurrentPos(base::as_byte_span("not json"));
logger_ = FileNetLogObserver::CreateBoundedPreExisting(
scratch_dir.GetPath(), std::move(file), kLargeFileSize,
NetLogCaptureMode::kDefault, nullptr);
logger_->StartObserving(NetLog::Get());
base::ThreadPoolInstance::Get()->FlushForTesting();
EXPECT_TRUE(base::PathExists(log_path_));
EXPECT_TRUE(
base::PathExists(scratch_dir.GetPath().AppendASCII("constants.json")));
EXPECT_FALSE(base::PathExists(GetInprogressDirectory()));
TestClosure closure;
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
EXPECT_FALSE(base::PathExists(scratch_dir.GetPath()));
EXPECT_FALSE(base::PathExists(GetInprogressDirectory()));
}
TEST_F(FileNetLogObserverBoundedTest, LargeWriteQueueSize) {
TestClosure closure;
uint64_t total_file_size = 0x8000000000000005;
CreateAndStartObserving(nullptr, total_file_size, kTotalNumFiles);
AddEntries(logger_.get(), 3, kDummyEventSize);
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
ReadNetLogFromDisk(log_path_));
ASSERT_EQ(3u, log->events->size());
}
void AddEntriesViaNetLog(NetLog* net_log, int num_entries) {
for (int i = 0; i < num_entries; i++) {
net_log->AddGlobalEntry(NetLogEventType::PAC_JAVASCRIPT_ERROR);
}
}
TEST_P(FileNetLogObserverTest, AddEventsFromMultipleThreadsWithStopObserving) {
const size_t kNumThreads = 10;
std::vector<std::unique_ptr<base::Thread>> threads(kNumThreads);
for (size_t i = 0; i < threads.size(); ++i) {
threads[i] = std::make_unique<base::Thread>("WorkerThread" +
base::NumberToString(i));
threads[i]->Start();
threads[i]->WaitUntilThreadStarted();
}
CreateAndStartObserving(nullptr);
const size_t kNumEventsAddedPerThread = 200;
for (size_t i = 0; i < kNumThreads; ++i) {
threads[i]->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&AddEntriesViaNetLog, base::Unretained(NetLog::Get()),
kNumEventsAddedPerThread));
}
TestClosure closure;
logger_->StopObserving(nullptr, closure.closure());
closure.WaitForResult();
threads.clear();
ASSERT_TRUE(LogFileExists());
}
TEST_P(FileNetLogObserverTest,
AddEventsFromMultipleThreadsWithoutStopObserving) {
const size_t kNumThreads = 10;
std::vector<std::unique_ptr<base::Thread>> threads(kNumThreads);
for (size_t i = 0; i < threads.size(); ++i) {
threads[i] = std::make_unique<base::Thread>("WorkerThread" +
base::NumberToString(i));
threads[i]->Start();
threads[i]->WaitUntilThreadStarted();
}
CreateAndStartObserving(nullptr);
const size_t kNumEventsAddedPerThread = 200;
for (size_t i = 0; i < kNumThreads; ++i) {
threads[i]->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&AddEntriesViaNetLog, base::Unretained(NetLog::Get()),
kNumEventsAddedPerThread));
}
logger_.reset();
threads.clear();
ASSERT_FALSE(LogFileExists());
}
}
}