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