xref: /aosp_15_r20/external/cronet/net/log/file_net_log_observer_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/log/file_net_log_observer.h"
6 
7 #include <string>
8 #include <vector>
9 
10 #include "base/files/file.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_file.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/functional/bind.h"
16 #include "base/functional/callback.h"
17 #include "base/json/json_reader.h"
18 #include "base/json/json_writer.h"
19 #include "base/memory/raw_ptr.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/task/thread_pool/thread_pool_instance.h"
24 #include "base/test/gmock_expected_support.h"
25 #include "base/threading/thread.h"
26 #include "base/types/expected.h"
27 #include "base/types/expected_macros.h"
28 #include "base/values.h"
29 #include "build/build_config.h"
30 #include "net/base/test_completion_callback.h"
31 #include "net/log/net_log.h"
32 #include "net/log/net_log_entry.h"
33 #include "net/log/net_log_event_type.h"
34 #include "net/log/net_log_source.h"
35 #include "net/log/net_log_source_type.h"
36 #include "net/log/net_log_util.h"
37 #include "net/log/net_log_values.h"
38 #include "net/test/test_with_task_environment.h"
39 #include "net/url_request/url_request.h"
40 #include "net/url_request/url_request_context.h"
41 #include "net/url_request/url_request_test_util.h"
42 #include "testing/gtest/include/gtest/gtest.h"
43 
44 namespace net {
45 
46 namespace {
47 
48 // Indicates the number of event files used in test cases.
49 const int kTotalNumFiles = 10;
50 
51 // Used to set the total file size maximum in test cases where the file size
52 // doesn't matter.
53 const int kLargeFileSize = 100000000;
54 
55 // Used to set the size of events to be sent to the observer in test cases
56 // where event size doesn't matter.
57 const size_t kDummyEventSize = 150;
58 
59 // Adds |num_entries| to |logger|. The "inverse" of this is VerifyEventsInLog().
AddEntries(FileNetLogObserver * logger,int num_entries,size_t entry_size)60 void AddEntries(FileNetLogObserver* logger,
61                 int num_entries,
62                 size_t entry_size) {
63   // Get base size of event.
64   const int kDummyId = 0;
65   NetLogSource source(NetLogSourceType::HTTP2_SESSION, kDummyId);
66   NetLogEntry base_entry(NetLogEventType::PAC_JAVASCRIPT_ERROR, source,
67                          NetLogEventPhase::BEGIN, base::TimeTicks::Now(),
68                          NetLogParamsWithString("message", ""));
69   base::Value::Dict value = base_entry.ToDict();
70   std::string json;
71   base::JSONWriter::Write(value, &json);
72   size_t base_entry_size = json.size();
73 
74   // The maximum value of base::TimeTicks::Now() will be the maximum value of
75   // int64_t, and if the maximum number of digits are included, the
76   // |base_entry_size| could be up to 136 characters. Check that the event
77   // format does not include additional padding.
78   DCHECK_LE(base_entry_size, 136u);
79 
80   // |entry_size| should be at least as big as the largest possible base
81   // entry.
82   EXPECT_GE(entry_size, 136u);
83 
84   // |entry_size| cannot be smaller than the minimum event size.
85   EXPECT_GE(entry_size, base_entry_size);
86 
87   for (int i = 0; i < num_entries; i++) {
88     source = NetLogSource(NetLogSourceType::HTTP2_SESSION, i);
89     std::string id = base::NumberToString(i);
90 
91     // String size accounts for the number of digits in id so that all events
92     // are the same size.
93     std::string message =
94         std::string(entry_size - base_entry_size - id.size() + 1, 'x');
95     NetLogEntry entry(NetLogEventType::PAC_JAVASCRIPT_ERROR, source,
96                       NetLogEventPhase::BEGIN, base::TimeTicks::Now(),
97                       NetLogParamsWithString("message", message));
98     logger->OnAddEntry(entry);
99   }
100 }
101 
102 // ParsedNetLog holds the parsed contents of a NetLog file (constants, events,
103 // and polled data).
104 struct ParsedNetLog {
105   base::expected<void, std::string> InitFromFileContents(
106       const std::string& input);
107   const base::Value::Dict* GetEvent(size_t i) const;
108 
109   // Initializes the ParsedNetLog by parsing a JSON file.
110   // Owner for the Value tree and a dictionary for the entire netlog.
111   base::Value root;
112 
113   // The constants dictionary.
114   raw_ptr<const base::Value::Dict> constants = nullptr;
115 
116   // The events list.
117   raw_ptr<const base::Value::List> events = nullptr;
118 
119   // The optional polled data (may be nullptr).
120   raw_ptr<const base::Value::Dict> polled_data = nullptr;
121 };
122 
InitFromFileContents(const std::string & input)123 base::expected<void, std::string> ParsedNetLog::InitFromFileContents(
124     const std::string& input) {
125   if (input.empty()) {
126     return base::unexpected("input is empty");
127   }
128 
129   ASSIGN_OR_RETURN(root, base::JSONReader::ReadAndReturnValueWithError(input),
130                    &base::JSONReader::Error::message);
131 
132   const base::Value::Dict* dict = root.GetIfDict();
133   if (!dict) {
134     return base::unexpected("Not a dictionary");
135   }
136 
137   events = dict->FindListByDottedPath("events");
138   if (!events) {
139     return base::unexpected("No events list");
140   }
141 
142   constants = dict->FindDictByDottedPath("constants");
143   if (!constants) {
144     return base::unexpected("No constants dictionary");
145   }
146 
147   // Polled data is optional (ignore success).
148   polled_data = dict->FindDictByDottedPath("polledData");
149 
150   return base::ok();
151 }
152 
153 // Returns the event at index |i|, or nullptr if there is none.
GetEvent(size_t i) const154 const base::Value::Dict* ParsedNetLog::GetEvent(size_t i) const {
155   if (!events || i >= events->size())
156     return nullptr;
157 
158   return (*events)[i].GetIfDict();
159 }
160 
161 // Creates a ParsedNetLog by reading a NetLog from a file. Returns nullptr on
162 // failure.
ReadNetLogFromDisk(const base::FilePath & log_path)163 base::expected<std::unique_ptr<ParsedNetLog>, std::string> ReadNetLogFromDisk(
164     const base::FilePath& log_path) {
165   std::string input;
166   if (!base::ReadFileToString(log_path, &input)) {
167     return base::unexpected("Failed reading file: " +
168                             base::UTF16ToUTF8(log_path.LossyDisplayName()));
169   }
170 
171   std::unique_ptr<ParsedNetLog> result = std::make_unique<ParsedNetLog>();
172 
173   RETURN_IF_ERROR(result->InitFromFileContents(input));
174   return result;
175 }
176 
177 // Checks that |log| contains events as emitted by AddEntries() above.
178 // |num_events_emitted| corresponds to |num_entries| of AddEntries(). Whereas
179 // |num_events_saved| is the expected number of events that have actually been
180 // written to the log (post-truncation).
VerifyEventsInLog(const ParsedNetLog * log,size_t num_events_emitted,size_t num_events_saved)181 void VerifyEventsInLog(const ParsedNetLog* log,
182                        size_t num_events_emitted,
183                        size_t num_events_saved) {
184   ASSERT_TRUE(log);
185   ASSERT_LE(num_events_saved, num_events_emitted);
186   ASSERT_EQ(num_events_saved, log->events->size());
187 
188   // The last |num_events_saved| should all be sequential, with the last one
189   // being numbered |num_events_emitted - 1|.
190   for (size_t i = 0; i < num_events_saved; ++i) {
191     const base::Value::Dict* event = log->GetEvent(i);
192     ASSERT_TRUE(event);
193 
194     size_t expected_source_id = num_events_emitted - num_events_saved + i;
195 
196     std::optional<int> id_value = event->FindIntByDottedPath("source.id");
197     ASSERT_EQ(static_cast<int>(expected_source_id), id_value);
198   }
199 }
200 
201 // Helper that checks whether |dict| has a string property at |key| having
202 // |value|.
ExpectDictionaryContainsProperty(const base::Value::Dict & dict,const std::string & key,const std::string & value)203 void ExpectDictionaryContainsProperty(const base::Value::Dict& dict,
204                                       const std::string& key,
205                                       const std::string& value) {
206   const std::string* actual_value = dict.FindStringByDottedPath(key);
207   ASSERT_EQ(value, *actual_value);
208 }
209 
210 // Used for tests that are common to both bounded and unbounded modes of the
211 // the FileNetLogObserver. The param is true if bounded mode is used.
212 class FileNetLogObserverTest : public ::testing::TestWithParam<bool>,
213                                public WithTaskEnvironment {
214  public:
SetUp()215   void SetUp() override {
216     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
217     log_path_ = temp_dir_.GetPath().AppendASCII("net-log.json");
218   }
219 
TearDown()220   void TearDown() override {
221     logger_.reset();
222     // FileNetLogObserver destructor might post to message loop.
223     RunUntilIdle();
224   }
225 
IsBounded() const226   bool IsBounded() const { return GetParam(); }
227 
CreateAndStartObserving(std::unique_ptr<base::Value::Dict> constants,NetLogCaptureMode capture_mode=NetLogCaptureMode::kDefault)228   void CreateAndStartObserving(
229       std::unique_ptr<base::Value::Dict> constants,
230       NetLogCaptureMode capture_mode = NetLogCaptureMode::kDefault) {
231     if (IsBounded()) {
232       logger_ = FileNetLogObserver::CreateBoundedForTests(
233           log_path_, kLargeFileSize, kTotalNumFiles, capture_mode,
234           std::move(constants));
235     } else {
236       logger_ = FileNetLogObserver::CreateUnbounded(log_path_, capture_mode,
237                                                     std::move(constants));
238     }
239 
240     logger_->StartObserving(NetLog::Get());
241   }
242 
CreateAndStartObservingPreExisting(std::unique_ptr<base::Value::Dict> constants)243   void CreateAndStartObservingPreExisting(
244       std::unique_ptr<base::Value::Dict> constants) {
245     ASSERT_TRUE(scratch_dir_.CreateUniqueTempDir());
246 
247     base::File file(log_path_,
248                     base::File::FLAG_CREATE | base::File::FLAG_WRITE);
249     EXPECT_TRUE(file.IsValid());
250     // Stick in some nonsense to make sure the file gets cleared properly
251     file.Write(0, "not json", 8);
252 
253     if (IsBounded()) {
254       logger_ = FileNetLogObserver::CreateBoundedPreExisting(
255           scratch_dir_.GetPath(), std::move(file), kLargeFileSize,
256           NetLogCaptureMode::kDefault, std::move(constants));
257     } else {
258       logger_ = FileNetLogObserver::CreateUnboundedPreExisting(
259           std::move(file), NetLogCaptureMode::kDefault, std::move(constants));
260     }
261 
262     logger_->StartObserving(NetLog::Get());
263   }
264 
LogFileExists()265   bool LogFileExists() {
266     // The log files are written by a sequenced task runner. Drain all the
267     // scheduled tasks to ensure that the file writing ones have run before
268     // checking if they exist.
269     base::ThreadPoolInstance::Get()->FlushForTesting();
270     return base::PathExists(log_path_);
271   }
272 
273  protected:
274   std::unique_ptr<FileNetLogObserver> logger_;
275   base::ScopedTempDir temp_dir_;
276   base::ScopedTempDir scratch_dir_;  // used for bounded + preexisting
277   base::FilePath log_path_;
278 };
279 
280 // Used for tests that are exclusive to the bounded mode of FileNetLogObserver.
281 class FileNetLogObserverBoundedTest : public ::testing::Test,
282                                       public WithTaskEnvironment {
283  public:
SetUp()284   void SetUp() override {
285     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
286     log_path_ = temp_dir_.GetPath().AppendASCII("net-log.json");
287   }
288 
TearDown()289   void TearDown() override {
290     logger_.reset();
291     // FileNetLogObserver destructor might post to message loop.
292     RunUntilIdle();
293   }
294 
CreateAndStartObserving(std::unique_ptr<base::Value::Dict> constants,uint64_t total_file_size,int num_files)295   void CreateAndStartObserving(std::unique_ptr<base::Value::Dict> constants,
296                                uint64_t total_file_size,
297                                int num_files) {
298     logger_ = FileNetLogObserver::CreateBoundedForTests(
299         log_path_, total_file_size, num_files, NetLogCaptureMode::kDefault,
300         std::move(constants));
301     logger_->StartObserving(NetLog::Get());
302   }
303 
304   // Returns the path for an internally directory created for bounded logs (this
305   // needs to be kept in sync with the implementation).
GetInprogressDirectory() const306   base::FilePath GetInprogressDirectory() const {
307     return log_path_.AddExtension(FILE_PATH_LITERAL(".inprogress"));
308   }
309 
GetEventFilePath(int index) const310   base::FilePath GetEventFilePath(int index) const {
311     return GetInprogressDirectory().AppendASCII(
312         "event_file_" + base::NumberToString(index) + ".json");
313   }
314 
GetEndNetlogPath() const315   base::FilePath GetEndNetlogPath() const {
316     return GetInprogressDirectory().AppendASCII("end_netlog.json");
317   }
318 
GetConstantsPath() const319   base::FilePath GetConstantsPath() const {
320     return GetInprogressDirectory().AppendASCII("constants.json");
321   }
322 
323 
324  protected:
325   std::unique_ptr<FileNetLogObserver> logger_;
326   base::FilePath log_path_;
327 
328  private:
329   base::ScopedTempDir temp_dir_;
330 };
331 
332 // Instantiates each FileNetLogObserverTest to use bounded and unbounded modes.
333 INSTANTIATE_TEST_SUITE_P(All,
334                          FileNetLogObserverTest,
335                          ::testing::Values(true, false));
336 
337 // Tests deleting a FileNetLogObserver without first calling StopObserving().
TEST_P(FileNetLogObserverTest,ObserverDestroyedWithoutStopObserving)338 TEST_P(FileNetLogObserverTest, ObserverDestroyedWithoutStopObserving) {
339   CreateAndStartObserving(nullptr);
340 
341   // Send dummy event
342   AddEntries(logger_.get(), 1, kDummyEventSize);
343 
344   // The log files should have been started.
345   ASSERT_TRUE(LogFileExists());
346 
347   logger_.reset();
348 
349   // When the logger is re-set without having called StopObserving(), the
350   // partially written log files are deleted.
351   ASSERT_FALSE(LogFileExists());
352 }
353 
354 // Same but with pre-existing file.
TEST_P(FileNetLogObserverTest,ObserverDestroyedWithoutStopObservingPreExisting)355 TEST_P(FileNetLogObserverTest,
356        ObserverDestroyedWithoutStopObservingPreExisting) {
357   CreateAndStartObservingPreExisting(nullptr);
358 
359   // Send dummy event
360   AddEntries(logger_.get(), 1, kDummyEventSize);
361 
362   // The log files should have been started.
363   ASSERT_TRUE(LogFileExists());
364 
365   // Should also have the scratch dir, if bounded. (Can be checked since
366   // LogFileExists flushed the thread pool).
367   if (IsBounded()) {
368     ASSERT_TRUE(base::PathExists(scratch_dir_.GetPath()));
369   }
370 
371   logger_.reset();
372 
373   // Unlike in the non-preexisting case, the output file isn't deleted here,
374   // since the process running the observer likely won't have the sandbox
375   // permission to do so.
376   ASSERT_TRUE(LogFileExists());
377   if (IsBounded()) {
378     ASSERT_FALSE(base::PathExists(scratch_dir_.GetPath()));
379   }
380 }
381 
382 // Tests calling StopObserving() with a null closure.
TEST_P(FileNetLogObserverTest,StopObservingNullClosure)383 TEST_P(FileNetLogObserverTest, StopObservingNullClosure) {
384   CreateAndStartObserving(nullptr);
385 
386   // Send dummy event
387   AddEntries(logger_.get(), 1, kDummyEventSize);
388 
389   // The log files should have been started.
390   ASSERT_TRUE(LogFileExists());
391 
392   logger_->StopObserving(nullptr, base::OnceClosure());
393 
394   logger_.reset();
395 
396   // Since the logger was explicitly stopped, its files should still exist.
397   ASSERT_TRUE(LogFileExists());
398 }
399 
400 // Tests creating a FileNetLogObserver using an invalid (can't be written to)
401 // path.
TEST_P(FileNetLogObserverTest,InitLogWithInvalidPath)402 TEST_P(FileNetLogObserverTest, InitLogWithInvalidPath) {
403   // Use a path to a non-existent directory.
404   log_path_ = temp_dir_.GetPath().AppendASCII("bogus").AppendASCII("path");
405 
406   CreateAndStartObserving(nullptr);
407 
408   // Send dummy event
409   AddEntries(logger_.get(), 1, kDummyEventSize);
410 
411   // No log files should have been written, as the log writer will not create
412   // missing directories.
413   ASSERT_FALSE(LogFileExists());
414 
415   logger_->StopObserving(nullptr, base::OnceClosure());
416 
417   logger_.reset();
418 
419   // There should still be no files.
420   ASSERT_FALSE(LogFileExists());
421 }
422 
TEST_P(FileNetLogObserverTest,GeneratesValidJSONWithNoEvents)423 TEST_P(FileNetLogObserverTest, GeneratesValidJSONWithNoEvents) {
424   TestClosure closure;
425 
426   CreateAndStartObserving(nullptr);
427 
428   logger_->StopObserving(nullptr, closure.closure());
429 
430   closure.WaitForResult();
431 
432   // Verify the written log.
433   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
434                        ReadNetLogFromDisk(log_path_));
435   ASSERT_EQ(0u, log->events->size());
436 }
437 
TEST_P(FileNetLogObserverTest,GeneratesValidJSONWithOneEvent)438 TEST_P(FileNetLogObserverTest, GeneratesValidJSONWithOneEvent) {
439   TestClosure closure;
440 
441   CreateAndStartObserving(nullptr);
442 
443   // Send dummy event.
444   AddEntries(logger_.get(), 1, kDummyEventSize);
445 
446   logger_->StopObserving(nullptr, closure.closure());
447 
448   closure.WaitForResult();
449 
450   // Verify the written log.
451   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
452                        ReadNetLogFromDisk(log_path_));
453   ASSERT_EQ(1u, log->events->size());
454 }
455 
TEST_P(FileNetLogObserverTest,GeneratesValidJSONWithOneEventPreExisting)456 TEST_P(FileNetLogObserverTest, GeneratesValidJSONWithOneEventPreExisting) {
457   TestClosure closure;
458 
459   CreateAndStartObservingPreExisting(nullptr);
460 
461   // Send dummy event.
462   AddEntries(logger_.get(), 1, kDummyEventSize);
463 
464   logger_->StopObserving(nullptr, closure.closure());
465 
466   closure.WaitForResult();
467 
468   // Verify the written log.
469   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
470                        ReadNetLogFromDisk(log_path_));
471   ASSERT_EQ(1u, log->events->size());
472 }
473 
TEST_P(FileNetLogObserverTest,PreExistingFileBroken)474 TEST_P(FileNetLogObserverTest, PreExistingFileBroken) {
475   // Test that pre-existing output file not being successfully open is
476   // tolerated.
477   ASSERT_TRUE(scratch_dir_.CreateUniqueTempDir());
478   base::File file;
479   EXPECT_FALSE(file.IsValid());
480   if (IsBounded())
481     logger_ = FileNetLogObserver::CreateBoundedPreExisting(
482         scratch_dir_.GetPath(), std::move(file), kLargeFileSize,
483         NetLogCaptureMode::kDefault, nullptr);
484   else
485     logger_ = FileNetLogObserver::CreateUnboundedPreExisting(
486         std::move(file), NetLogCaptureMode::kDefault, nullptr);
487   logger_->StartObserving(NetLog::Get());
488 
489   // Send dummy event.
490   AddEntries(logger_.get(), 1, kDummyEventSize);
491   TestClosure closure;
492   logger_->StopObserving(nullptr, closure.closure());
493   closure.WaitForResult();
494 }
495 
TEST_P(FileNetLogObserverTest,CustomConstants)496 TEST_P(FileNetLogObserverTest, CustomConstants) {
497   TestClosure closure;
498 
499   const char kConstantKey[] = "magic";
500   const char kConstantString[] = "poney";
501   base::Value::Dict constants;
502   constants.SetByDottedPath(kConstantKey, kConstantString);
503 
504   CreateAndStartObserving(
505       std::make_unique<base::Value::Dict>(std::move(constants)));
506 
507   logger_->StopObserving(nullptr, closure.closure());
508 
509   closure.WaitForResult();
510 
511   // Verify the written log.
512   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
513                        ReadNetLogFromDisk(log_path_));
514 
515   // Check that custom constant was correctly printed.
516   ExpectDictionaryContainsProperty(*log->constants, kConstantKey,
517                                    kConstantString);
518 }
519 
TEST_P(FileNetLogObserverTest,GeneratesValidJSONWithPolledData)520 TEST_P(FileNetLogObserverTest, GeneratesValidJSONWithPolledData) {
521   TestClosure closure;
522 
523   CreateAndStartObserving(nullptr);
524 
525   // Create dummy polled data
526   const char kDummyPolledDataPath[] = "dummy_path";
527   const char kDummyPolledDataString[] = "dummy_info";
528   base::Value::Dict dummy_polled_data;
529   dummy_polled_data.SetByDottedPath(kDummyPolledDataPath,
530                                     kDummyPolledDataString);
531 
532   logger_->StopObserving(
533       std::make_unique<base::Value>(std::move(dummy_polled_data)),
534       closure.closure());
535 
536   closure.WaitForResult();
537 
538   // Verify the written log.
539   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
540                        ReadNetLogFromDisk(log_path_));
541   ASSERT_EQ(0u, log->events->size());
542 
543   // Make sure additional information is present and validate it.
544   ASSERT_TRUE(log->polled_data);
545   ExpectDictionaryContainsProperty(*log->polled_data, kDummyPolledDataPath,
546                                    kDummyPolledDataString);
547 }
548 
549 // Ensure that the Capture Mode is recorded as a constant in the NetLog.
TEST_P(FileNetLogObserverTest,LogModeRecorded)550 TEST_P(FileNetLogObserverTest, LogModeRecorded) {
551   struct TestCase {
552     NetLogCaptureMode capture_mode;
553     const char* expected_value;
554   } test_cases[] = {// Challenges that result in success results.
555                     {NetLogCaptureMode::kEverything, "Everything"},
556                     {NetLogCaptureMode::kIncludeSensitive, "IncludeSensitive"},
557                     {NetLogCaptureMode::kDefault, "Default"}};
558 
559   TestClosure closure;
560   for (const auto& test_case : test_cases) {
561     CreateAndStartObserving(nullptr, test_case.capture_mode);
562     logger_->StopObserving(nullptr, closure.closure());
563     closure.WaitForResult();
564     ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
565                          ReadNetLogFromDisk(log_path_));
566     ExpectDictionaryContainsProperty(*log->constants, "logCaptureMode",
567                                      test_case.expected_value);
568   }
569 }
570 
571 // Adds events concurrently from several different threads. The exact order of
572 // events seen by this test is non-deterministic.
TEST_P(FileNetLogObserverTest,AddEventsFromMultipleThreads)573 TEST_P(FileNetLogObserverTest, AddEventsFromMultipleThreads) {
574   const size_t kNumThreads = 10;
575   std::vector<std::unique_ptr<base::Thread>> threads(kNumThreads);
576 
577 #if BUILDFLAG(IS_FUCHSIA)
578   // TODO(https://crbug.com/959245): Diagnosting logging to determine where
579   // this test sometimes hangs.
580   LOG(ERROR) << "Create and start threads.";
581 #endif
582 
583   // Start all the threads. Waiting for them to start is to hopefuly improve
584   // the odds of hitting interesting races once events start being added.
585   for (size_t i = 0; i < threads.size(); ++i) {
586     threads[i] = std::make_unique<base::Thread>("WorkerThread" +
587                                                 base::NumberToString(i));
588     threads[i]->Start();
589     threads[i]->WaitUntilThreadStarted();
590   }
591 
592 #if BUILDFLAG(IS_FUCHSIA)
593   LOG(ERROR) << "Create and start observing.";
594 #endif
595 
596   CreateAndStartObserving(nullptr);
597 
598   const size_t kNumEventsAddedPerThread = 200;
599 
600 #if BUILDFLAG(IS_FUCHSIA)
601   LOG(ERROR) << "Posting tasks.";
602 #endif
603 
604   // Add events in parallel from all the threads.
605   for (size_t i = 0; i < kNumThreads; ++i) {
606     threads[i]->task_runner()->PostTask(
607         FROM_HERE, base::BindOnce(&AddEntries, base::Unretained(logger_.get()),
608                                   kNumEventsAddedPerThread, kDummyEventSize));
609   }
610 
611 #if BUILDFLAG(IS_FUCHSIA)
612   LOG(ERROR) << "Joining all threads.";
613 #endif
614 
615   // Join all the threads.
616   threads.clear();
617 
618 #if BUILDFLAG(IS_FUCHSIA)
619   LOG(ERROR) << "Stop observing.";
620 #endif
621 
622   // Stop observing.
623   TestClosure closure;
624   logger_->StopObserving(nullptr, closure.closure());
625   closure.WaitForResult();
626 
627 #if BUILDFLAG(IS_FUCHSIA)
628   LOG(ERROR) << "Read log from disk and verify.";
629 #endif
630 
631   // Verify the written log.
632   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
633                        ReadNetLogFromDisk(log_path_));
634   // Check that the expected number of events were written to disk.
635   EXPECT_EQ(kNumEventsAddedPerThread * kNumThreads, log->events->size());
636 
637 #if BUILDFLAG(IS_FUCHSIA)
638   LOG(ERROR) << "Teardown.";
639 #endif
640 }
641 
642 // Sends enough events to the observer to completely fill one file, but not
643 // write any events to an additional file. Checks the file bounds.
TEST_F(FileNetLogObserverBoundedTest,EqualToOneFile)644 TEST_F(FileNetLogObserverBoundedTest, EqualToOneFile) {
645   // The total size of the events is equal to the size of one file.
646   // |kNumEvents| * |kEventSize| = |kTotalFileSize| / |kTotalNumEvents|
647   const int kTotalFileSize = 5000;
648   const int kNumEvents = 2;
649   const int kEventSize = 250;
650   TestClosure closure;
651 
652   CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
653 
654   AddEntries(logger_.get(), kNumEvents, kEventSize);
655   logger_->StopObserving(nullptr, closure.closure());
656 
657   closure.WaitForResult();
658 
659   // Verify the written log.
660   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
661                        ReadNetLogFromDisk(log_path_));
662   VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
663 }
664 
665 // Sends enough events to fill one file, and partially fill a second file.
666 // Checks the file bounds and writing to a new file.
TEST_F(FileNetLogObserverBoundedTest,OneEventOverOneFile)667 TEST_F(FileNetLogObserverBoundedTest, OneEventOverOneFile) {
668   // The total size of the events is greater than the size of one file, and
669   // less than the size of two files. The total size of all events except one
670   // is equal to the size of one file, so the last event will be the only event
671   // in the second file.
672   // (|kNumEvents| - 1) * kEventSize = |kTotalFileSize| / |kTotalNumEvents|
673   const int kTotalFileSize = 6000;
674   const int kNumEvents = 4;
675   const int kEventSize = 200;
676   TestClosure closure;
677 
678   CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
679 
680   AddEntries(logger_.get(), kNumEvents, kEventSize);
681 
682   logger_->StopObserving(nullptr, closure.closure());
683 
684   closure.WaitForResult();
685 
686   // Verify the written log.
687   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
688                        ReadNetLogFromDisk(log_path_));
689   VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
690 }
691 
692 // Sends enough events to the observer to completely fill two files.
TEST_F(FileNetLogObserverBoundedTest,EqualToTwoFiles)693 TEST_F(FileNetLogObserverBoundedTest, EqualToTwoFiles) {
694   // The total size of the events is equal to the total size of two files.
695   // |kNumEvents| * |kEventSize| = 2 * |kTotalFileSize| / |kTotalNumEvents|
696   const int kTotalFileSize = 6000;
697   const int kNumEvents = 6;
698   const int kEventSize = 200;
699   TestClosure closure;
700 
701   CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
702 
703   AddEntries(logger_.get(), kNumEvents, kEventSize);
704 
705   logger_->StopObserving(nullptr, closure.closure());
706 
707   closure.WaitForResult();
708 
709   // Verify the written log.
710   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
711                        ReadNetLogFromDisk(log_path_));
712   VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
713 }
714 
715 // Sends exactly enough events to the observer to completely fill all files,
716 // so that all events fit into the event files and no files need to be
717 // overwritten.
TEST_F(FileNetLogObserverBoundedTest,FillAllFilesNoOverwriting)718 TEST_F(FileNetLogObserverBoundedTest, FillAllFilesNoOverwriting) {
719   // The total size of events is equal to the total size of all files.
720   // |kEventSize| * |kNumEvents| = |kTotalFileSize|
721   const int kTotalFileSize = 10000;
722   const int kEventSize = 200;
723   const int kFileSize = kTotalFileSize / kTotalNumFiles;
724   const int kNumEvents = kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1);
725   TestClosure closure;
726 
727   CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
728 
729   AddEntries(logger_.get(), kNumEvents, kEventSize);
730 
731   logger_->StopObserving(nullptr, closure.closure());
732 
733   closure.WaitForResult();
734 
735   // Verify the written log.
736   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
737                        ReadNetLogFromDisk(log_path_));
738   VerifyEventsInLog(log.get(), kNumEvents, kNumEvents);
739 }
740 
741 // Sends more events to the observer than will fill the WriteQueue, forcing the
742 // queue to drop an event. Checks that the queue drops the oldest event.
TEST_F(FileNetLogObserverBoundedTest,DropOldEventsFromWriteQueue)743 TEST_F(FileNetLogObserverBoundedTest, DropOldEventsFromWriteQueue) {
744   // The total size of events is greater than the WriteQueue's memory limit, so
745   // the oldest event must be dropped from the queue and not written to any
746   // file.
747   // |kNumEvents| * |kEventSize| > |kTotalFileSize| * 2
748   const int kTotalFileSize = 1000;
749   const int kNumEvents = 11;
750   const int kEventSize = 200;
751   const int kFileSize = kTotalFileSize / kTotalNumFiles;
752   TestClosure closure;
753 
754   CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
755 
756   AddEntries(logger_.get(), kNumEvents, kEventSize);
757 
758   logger_->StopObserving(nullptr, closure.closure());
759 
760   closure.WaitForResult();
761 
762   // Verify the written log.
763   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
764                        ReadNetLogFromDisk(log_path_));
765   VerifyEventsInLog(
766       log.get(), kNumEvents,
767       static_cast<size_t>(kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1)));
768 }
769 
770 // Sends twice as many events as will fill all files to the observer, so that
771 // all of the event files will be filled twice, and every file will be
772 // overwritten.
TEST_F(FileNetLogObserverBoundedTest,OverwriteAllFiles)773 TEST_F(FileNetLogObserverBoundedTest, OverwriteAllFiles) {
774   // The total size of the events is much greater than twice the number of
775   // events that can fit in the event files, to make sure that the extra events
776   // are written to a file, not just dropped from the queue.
777   // |kNumEvents| * |kEventSize| >= 2 * |kTotalFileSize|
778   const int kTotalFileSize = 6000;
779   const int kNumEvents = 60;
780   const int kEventSize = 200;
781   const int kFileSize = kTotalFileSize / kTotalNumFiles;
782   TestClosure closure;
783 
784   CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
785 
786   AddEntries(logger_.get(), kNumEvents, kEventSize);
787 
788   logger_->StopObserving(nullptr, closure.closure());
789 
790   closure.WaitForResult();
791 
792   // Check that the minimum number of events that should fit in event files
793   // have been written to all files.
794   int events_per_file = (kFileSize - 1) / kEventSize + 1;
795   int events_in_last_file = (kNumEvents - 1) % events_per_file + 1;
796 
797   // Indicates the total number of events that should be written to all files.
798   int num_events_in_files =
799       (kTotalNumFiles - 1) * events_per_file + events_in_last_file;
800 
801   // Verify the written log.
802   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
803                        ReadNetLogFromDisk(log_path_));
804   VerifyEventsInLog(log.get(), kNumEvents,
805                     static_cast<size_t>(num_events_in_files));
806 }
807 
808 // Sends enough events to the observer to fill all event files, plus overwrite
809 // some files, without overwriting all of them. Checks that the FileWriter
810 // overwrites the file with the oldest events.
TEST_F(FileNetLogObserverBoundedTest,PartiallyOverwriteFiles)811 TEST_F(FileNetLogObserverBoundedTest, PartiallyOverwriteFiles) {
812   // The number of events sent to the observer is greater than the number of
813   // events that can fit into the event files, but the events can fit in less
814   // than twice the number of event files, so not every file will need to be
815   // overwritten.
816   // |kTotalFileSize| < |kNumEvents| * |kEventSize|
817   // |kNumEvents| * |kEventSize| <= (2 * |kTotalNumFiles| - 1) * |kFileSize|
818   const int kTotalFileSize = 6000;
819   const int kNumEvents = 50;
820   const int kEventSize = 200;
821   const int kFileSize = kTotalFileSize / kTotalNumFiles;
822   TestClosure closure;
823 
824   CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
825 
826   AddEntries(logger_.get(), kNumEvents, kEventSize);
827 
828   logger_->StopObserving(nullptr, closure.closure());
829 
830   closure.WaitForResult();
831 
832   // Check that the minimum number of events that should fit in event files
833   // have been written to a file.
834   int events_per_file = (kFileSize - 1) / kEventSize + 1;
835   int events_in_last_file = kNumEvents % events_per_file;
836   if (!events_in_last_file)
837     events_in_last_file = events_per_file;
838   int num_events_in_files =
839       (kTotalNumFiles - 1) * events_per_file + events_in_last_file;
840 
841   // Verify the written log.
842   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
843                        ReadNetLogFromDisk(log_path_));
844   VerifyEventsInLog(log.get(), kNumEvents,
845                     static_cast<size_t>(num_events_in_files));
846 }
847 
848 // Start logging in bounded mode. Create directories in places where the logger
849 // expects to create files, in order to cause that file creation to fail.
850 //
851 //   constants.json      -- succeess
852 //   event_file_0.json   -- fails to open
853 //   end_netlog.json     -- fails to open
TEST_F(FileNetLogObserverBoundedTest,SomeFilesFailToOpen)854 TEST_F(FileNetLogObserverBoundedTest, SomeFilesFailToOpen) {
855   // The total size of events is equal to the total size of all files.
856   // |kEventSize| * |kNumEvents| = |kTotalFileSize|
857   const int kTotalFileSize = 10000;
858   const int kEventSize = 200;
859   const int kFileSize = kTotalFileSize / kTotalNumFiles;
860   const int kNumEvents = kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1);
861   TestClosure closure;
862 
863   // Create directories as a means to block files from being created by logger.
864   EXPECT_TRUE(base::CreateDirectory(GetEventFilePath(0)));
865   EXPECT_TRUE(base::CreateDirectory(GetEndNetlogPath()));
866 
867   CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
868 
869   AddEntries(logger_.get(), kNumEvents, kEventSize);
870 
871   logger_->StopObserving(nullptr, closure.closure());
872 
873   closure.WaitForResult();
874 
875   // The written log is invalid (and hence can't be parsed). It is just the
876   // constants.
877   std::string log_contents;
878   ASSERT_TRUE(base::ReadFileToString(log_path_, &log_contents));
879   // TODO(eroman): Verify the partially written log file?
880 
881   // Even though FileNetLogObserver didn't create the directory itself, it will
882   // unconditionally delete it. The name should be uncommon enough for this be
883   // to reasonable.
884   EXPECT_FALSE(base::PathExists(GetInprogressDirectory()));
885 }
886 
887 // Start logging in bounded mode. Create a file at the path where the logger
888 // expects to create its inprogress directory to store event files. This will
889 // cause logging to completely break. open it.
TEST_F(FileNetLogObserverBoundedTest,InprogressDirectoryBlocked)890 TEST_F(FileNetLogObserverBoundedTest, InprogressDirectoryBlocked) {
891   // The total size of events is equal to the total size of all files.
892   // |kEventSize| * |kNumEvents| = |kTotalFileSize|
893   const int kTotalFileSize = 10000;
894   const int kEventSize = 200;
895   const int kFileSize = kTotalFileSize / kTotalNumFiles;
896   const int kNumEvents = kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1);
897   TestClosure closure;
898 
899   // By creating a file where a directory should be, it will not be possible to
900   // write any event files.
901   EXPECT_TRUE(base::WriteFile(GetInprogressDirectory(), "x"));
902 
903   CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
904 
905   AddEntries(logger_.get(), kNumEvents, kEventSize);
906 
907   logger_->StopObserving(nullptr, closure.closure());
908 
909   closure.WaitForResult();
910 
911   // There will be a log file at the final output, however it will be empty
912   // since nothing was written to the .inprogress directory.
913   std::string log_contents;
914   ASSERT_TRUE(base::ReadFileToString(log_path_, &log_contents));
915   EXPECT_EQ("", log_contents);
916 
917   // FileNetLogObserver unconditionally deletes the inprogress path (even though
918   // it didn't actually create this file and it was a file instead of a
919   // directory).
920   // TODO(eroman): Should it only delete if it is a file?
921   EXPECT_FALSE(base::PathExists(GetInprogressDirectory()));
922 }
923 
924 // Start logging in bounded mode. Create a file with the same name as the 0th
925 // events file. This will prevent any events from being written.
TEST_F(FileNetLogObserverBoundedTest,BlockEventsFile0)926 TEST_F(FileNetLogObserverBoundedTest, BlockEventsFile0) {
927   // The total size of events is equal to the total size of all files.
928   // |kEventSize| * |kNumEvents| = |kTotalFileSize|
929   const int kTotalFileSize = 10000;
930   const int kEventSize = 200;
931   const int kFileSize = kTotalFileSize / kTotalNumFiles;
932   const int kNumEvents = kTotalNumFiles * ((kFileSize - 1) / kEventSize + 1);
933   TestClosure closure;
934 
935   // Block the 0th events file.
936   EXPECT_TRUE(base::CreateDirectory(GetEventFilePath(0)));
937 
938   CreateAndStartObserving(nullptr, kTotalFileSize, kTotalNumFiles);
939 
940   AddEntries(logger_.get(), kNumEvents, kEventSize);
941 
942   logger_->StopObserving(nullptr, closure.closure());
943 
944   closure.WaitForResult();
945 
946   // Verify the written log.
947   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
948                        ReadNetLogFromDisk(log_path_));
949   ASSERT_EQ(0u, log->events->size());
950 }
951 
952 // Make sure that when using bounded mode with a pre-existing output file,
953 // a separate in-progress directory can be specified.
TEST_F(FileNetLogObserverBoundedTest,PreExistingUsesSpecifiedDir)954 TEST_F(FileNetLogObserverBoundedTest, PreExistingUsesSpecifiedDir) {
955   base::ScopedTempDir scratch_dir;
956   ASSERT_TRUE(scratch_dir.CreateUniqueTempDir());
957 
958   base::File file(log_path_, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
959   ASSERT_TRUE(file.IsValid());
960 
961   // Stick in some nonsense to make sure the file gets cleared properly
962   file.Write(0, "not json", 8);
963 
964   logger_ = FileNetLogObserver::CreateBoundedPreExisting(
965       scratch_dir.GetPath(), std::move(file), kLargeFileSize,
966       NetLogCaptureMode::kDefault, nullptr);
967   logger_->StartObserving(NetLog::Get());
968 
969   base::ThreadPoolInstance::Get()->FlushForTesting();
970   EXPECT_TRUE(base::PathExists(log_path_));
971   EXPECT_TRUE(
972       base::PathExists(scratch_dir.GetPath().AppendASCII("constants.json")));
973   EXPECT_FALSE(base::PathExists(GetInprogressDirectory()));
974 
975   TestClosure closure;
976   logger_->StopObserving(nullptr, closure.closure());
977   closure.WaitForResult();
978 
979   // Now the scratch dir should be gone, too.
980   EXPECT_FALSE(base::PathExists(scratch_dir.GetPath()));
981   EXPECT_FALSE(base::PathExists(GetInprogressDirectory()));
982 }
983 
984 // Creates a bounded log with a very large total size and verifies that events
985 // are not dropped. This is a regression test for https://crbug.com/959929 in
986 // which the WriteQueue size was calculated by the possibly overflowed
987 // expression |total_file_size * 2|.
TEST_F(FileNetLogObserverBoundedTest,LargeWriteQueueSize)988 TEST_F(FileNetLogObserverBoundedTest, LargeWriteQueueSize) {
989   TestClosure closure;
990 
991   // This is a large value such that multiplying it by 2 will overflow to a much
992   // smaller value (5).
993   uint64_t total_file_size = 0x8000000000000005;
994 
995   CreateAndStartObserving(nullptr, total_file_size, kTotalNumFiles);
996 
997   // Send 3 dummy events. This isn't a lot of data, however if WriteQueue was
998   // initialized using the overflowed value of |total_file_size * 2| (which is
999   // 5), then the effective limit would prevent any events from being written.
1000   AddEntries(logger_.get(), 3, kDummyEventSize);
1001 
1002   logger_->StopObserving(nullptr, closure.closure());
1003 
1004   closure.WaitForResult();
1005 
1006   // Verify the written log.
1007   ASSERT_OK_AND_ASSIGN(std::unique_ptr<ParsedNetLog> log,
1008                        ReadNetLogFromDisk(log_path_));
1009   ASSERT_EQ(3u, log->events->size());
1010 }
1011 
AddEntriesViaNetLog(NetLog * net_log,int num_entries)1012 void AddEntriesViaNetLog(NetLog* net_log, int num_entries) {
1013   for (int i = 0; i < num_entries; i++) {
1014     net_log->AddGlobalEntry(NetLogEventType::PAC_JAVASCRIPT_ERROR);
1015   }
1016 }
1017 
TEST_P(FileNetLogObserverTest,AddEventsFromMultipleThreadsWithStopObserving)1018 TEST_P(FileNetLogObserverTest, AddEventsFromMultipleThreadsWithStopObserving) {
1019   const size_t kNumThreads = 10;
1020   std::vector<std::unique_ptr<base::Thread>> threads(kNumThreads);
1021   // Start all the threads. Waiting for them to start is to hopefully improve
1022   // the odds of hitting interesting races once events start being added.
1023   for (size_t i = 0; i < threads.size(); ++i) {
1024     threads[i] = std::make_unique<base::Thread>("WorkerThread" +
1025                                                 base::NumberToString(i));
1026     threads[i]->Start();
1027     threads[i]->WaitUntilThreadStarted();
1028   }
1029 
1030   CreateAndStartObserving(nullptr);
1031 
1032   const size_t kNumEventsAddedPerThread = 200;
1033 
1034   // Add events in parallel from all the threads.
1035   for (size_t i = 0; i < kNumThreads; ++i) {
1036     threads[i]->task_runner()->PostTask(
1037         FROM_HERE,
1038         base::BindOnce(&AddEntriesViaNetLog, base::Unretained(NetLog::Get()),
1039                        kNumEventsAddedPerThread));
1040   }
1041 
1042   // Stop observing.
1043   TestClosure closure;
1044   logger_->StopObserving(nullptr, closure.closure());
1045   closure.WaitForResult();
1046 
1047   // Join all the threads.
1048   threads.clear();
1049 
1050   ASSERT_TRUE(LogFileExists());
1051 }
1052 
TEST_P(FileNetLogObserverTest,AddEventsFromMultipleThreadsWithoutStopObserving)1053 TEST_P(FileNetLogObserverTest,
1054        AddEventsFromMultipleThreadsWithoutStopObserving) {
1055   const size_t kNumThreads = 10;
1056   std::vector<std::unique_ptr<base::Thread>> threads(kNumThreads);
1057   // Start all the threads. Waiting for them to start is to hopefully improve
1058   // the odds of hitting interesting races once events start being added.
1059   for (size_t i = 0; i < threads.size(); ++i) {
1060     threads[i] = std::make_unique<base::Thread>("WorkerThread" +
1061                                                 base::NumberToString(i));
1062     threads[i]->Start();
1063     threads[i]->WaitUntilThreadStarted();
1064   }
1065 
1066   CreateAndStartObserving(nullptr);
1067 
1068   const size_t kNumEventsAddedPerThread = 200;
1069 
1070   // Add events in parallel from all the threads.
1071   for (size_t i = 0; i < kNumThreads; ++i) {
1072     threads[i]->task_runner()->PostTask(
1073         FROM_HERE,
1074         base::BindOnce(&AddEntriesViaNetLog, base::Unretained(NetLog::Get()),
1075                        kNumEventsAddedPerThread));
1076   }
1077 
1078   // Destroy logger.
1079   logger_.reset();
1080 
1081   // Join all the threads.
1082   threads.clear();
1083 
1084   // The log file doesn't exist since StopObserving() was not called.
1085   ASSERT_FALSE(LogFileExists());
1086 }
1087 
1088 }  // namespace
1089 
1090 }  // namespace net
1091