// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/net_log/net_log_temp_file.h" #include "base/basictypes.h" #include "base/command_line.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/json/json_reader.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/values.h" #include "build/build_config.h" #include "components/net_log/chrome_net_log.h" #include "net/log/net_log_capture_mode.h" #include "net/log/write_to_file_net_log_observer.h" #include "testing/gtest/include/gtest/gtest.h" namespace { const char kChannelString[] = "SomeChannel"; } // namespace namespace net_log { class TestNetLogTempFile : public NetLogTempFile { public: explicit TestNetLogTempFile(ChromeNetLog* chrome_net_log) : NetLogTempFile( chrome_net_log, base::CommandLine::ForCurrentProcess()->GetCommandLineString(), kChannelString), lie_about_net_export_log_directory_(false) { EXPECT_TRUE(net_log_temp_dir_.CreateUniqueTempDir()); } ~TestNetLogTempFile() override { EXPECT_TRUE(net_log_temp_dir_.Delete()); } // NetLogTempFile implementation: bool GetNetExportLogBaseDirectory(base::FilePath* path) const override { if (lie_about_net_export_log_directory_) return false; *path = net_log_temp_dir_.path(); return true; } void set_lie_about_net_export_log_directory( bool lie_about_net_export_log_directory) { lie_about_net_export_log_directory_ = lie_about_net_export_log_directory; } private: bool lie_about_net_export_log_directory_; base::ScopedTempDir net_log_temp_dir_; }; class NetLogTempFileTest : public ::testing::Test { public: NetLogTempFileTest() : net_log_(new ChromeNetLog( base::FilePath(), net::NetLogCaptureMode::Default(), base::CommandLine::ForCurrentProcess()->GetCommandLineString(), kChannelString)), net_log_temp_file_(new TestNetLogTempFile(net_log_.get())) { EXPECT_TRUE(net_log_temp_file_->SetUpNetExportLogPath()); net_export_log_ = net_log_temp_file_->log_path_; } std::string GetStateString() const { scoped_ptr<base::DictionaryValue> dict(net_log_temp_file_->GetState()); std::string state; EXPECT_TRUE(dict->GetString("state", &state)); return state; } std::string GetLogTypeString() const { scoped_ptr<base::DictionaryValue> dict(net_log_temp_file_->GetState()); std::string log_type; EXPECT_TRUE(dict->GetString("logType", &log_type)); return log_type; } // Make sure the export file has been created and is non-empty, as net // constants will always be written to it on creation. void VerifyNetExportLogExists() const { EXPECT_EQ(net_export_log_, net_log_temp_file_->log_path_); ASSERT_TRUE(base::PathExists(net_export_log_)); int64 file_size; // base::GetFileSize returns proper file size on open handles. ASSERT_TRUE(base::GetFileSize(net_export_log_, &file_size)); EXPECT_GT(file_size, 0); } // Make sure the export file has been created and a valid JSON file. This // should always be the case once logging has been stopped. void VerifyNetExportLogComplete() const { VerifyNetExportLogExists(); std::string log; ASSERT_TRUE(ReadFileToString(net_export_log_, &log)); base::JSONReader reader; scoped_ptr<base::Value> json = base::JSONReader::Read(log); EXPECT_TRUE(json); } // Verify state and GetFilePath return correct values if EnsureInit() fails. void VerifyFilePathAndStateAfterEnsureInitFailure() { EXPECT_EQ("UNINITIALIZED", GetStateString()); EXPECT_EQ(NetLogTempFile::STATE_UNINITIALIZED, net_log_temp_file_->state()); base::FilePath net_export_file_path; EXPECT_FALSE(net_log_temp_file_->GetFilePath(&net_export_file_path)); } // When we lie in NetExportLogExists, make sure state and GetFilePath return // correct values. void VerifyFilePathAndStateAfterEnsureInit() { EXPECT_EQ("NOT_LOGGING", GetStateString()); EXPECT_EQ(NetLogTempFile::STATE_NOT_LOGGING, net_log_temp_file_->state()); EXPECT_EQ("NONE", GetLogTypeString()); EXPECT_EQ(NetLogTempFile::LOG_TYPE_NONE, net_log_temp_file_->log_type()); base::FilePath net_export_file_path; EXPECT_FALSE(net_log_temp_file_->GetFilePath(&net_export_file_path)); EXPECT_FALSE(net_log_temp_file_->NetExportLogExists()); } // The following methods make sure the export file has been successfully // initialized by a DO_START command of the given type. void VerifyFileAndStateAfterDoStart() { VerifyFileAndStateAfterStart( NetLogTempFile::LOG_TYPE_NORMAL, "NORMAL", net::NetLogCaptureMode::IncludeCookiesAndCredentials()); } void VerifyFileAndStateAfterDoStartStripPrivateData() const { VerifyFileAndStateAfterStart(NetLogTempFile::LOG_TYPE_STRIP_PRIVATE_DATA, "STRIP_PRIVATE_DATA", net::NetLogCaptureMode::Default()); } void VerifyFileAndStateAfterDoStartLogBytes() const { VerifyFileAndStateAfterStart(NetLogTempFile::LOG_TYPE_LOG_BYTES, "LOG_BYTES", net::NetLogCaptureMode::IncludeSocketBytes()); } // Make sure the export file has been successfully initialized after DO_STOP // command following a DO_START command of the given type. void VerifyFileAndStateAfterDoStop() const { VerifyFileAndStateAfterDoStop(NetLogTempFile::LOG_TYPE_NORMAL, "NORMAL"); } void VerifyFileAndStateAfterDoStopWithStripPrivateData() const { VerifyFileAndStateAfterDoStop(NetLogTempFile::LOG_TYPE_STRIP_PRIVATE_DATA, "STRIP_PRIVATE_DATA"); } void VerifyFileAndStateAfterDoStopWithLogBytes() const { VerifyFileAndStateAfterDoStop(NetLogTempFile::LOG_TYPE_LOG_BYTES, "LOG_BYTES"); } scoped_ptr<ChromeNetLog> net_log_; // |net_log_temp_file_| is initialized after |net_log_| so that it can stop // obvserving on destruction. scoped_ptr<TestNetLogTempFile> net_log_temp_file_; base::FilePath net_export_log_; private: // Checks state after one of the DO_START* commands. void VerifyFileAndStateAfterStart( NetLogTempFile::LogType expected_log_type, const std::string& expected_log_type_string, net::NetLogCaptureMode expected_capture_mode) const { EXPECT_EQ(NetLogTempFile::STATE_LOGGING, net_log_temp_file_->state()); EXPECT_EQ("LOGGING", GetStateString()); EXPECT_EQ(expected_log_type, net_log_temp_file_->log_type()); EXPECT_EQ(expected_log_type_string, GetLogTypeString()); EXPECT_EQ(expected_capture_mode, net_log_temp_file_->write_to_file_observer_->capture_mode()); // Check GetFilePath returns false when still writing to the file. base::FilePath net_export_file_path; EXPECT_FALSE(net_log_temp_file_->GetFilePath(&net_export_file_path)); VerifyNetExportLogExists(); } void VerifyFileAndStateAfterDoStop( NetLogTempFile::LogType expected_log_type, const std::string& expected_log_type_string) const { EXPECT_EQ(NetLogTempFile::STATE_NOT_LOGGING, net_log_temp_file_->state()); EXPECT_EQ("NOT_LOGGING", GetStateString()); EXPECT_EQ(expected_log_type, net_log_temp_file_->log_type()); EXPECT_EQ(expected_log_type_string, GetLogTypeString()); base::FilePath net_export_file_path; EXPECT_TRUE(net_log_temp_file_->GetFilePath(&net_export_file_path)); EXPECT_EQ(net_export_log_, net_export_file_path); VerifyNetExportLogComplete(); } base::MessageLoop message_loop_; }; TEST_F(NetLogTempFileTest, EnsureInitFailure) { net_log_temp_file_->set_lie_about_net_export_log_directory(true); EXPECT_FALSE(net_log_temp_file_->EnsureInit()); VerifyFilePathAndStateAfterEnsureInitFailure(); net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_START); VerifyFilePathAndStateAfterEnsureInitFailure(); } TEST_F(NetLogTempFileTest, EnsureInitAllowStart) { EXPECT_TRUE(net_log_temp_file_->EnsureInit()); VerifyFilePathAndStateAfterEnsureInit(); // Calling EnsureInit() second time should be a no-op. EXPECT_TRUE(net_log_temp_file_->EnsureInit()); VerifyFilePathAndStateAfterEnsureInit(); // GetFilePath() should failed when there's no temp file. base::FilePath net_export_file_path; EXPECT_FALSE(net_log_temp_file_->GetFilePath(&net_export_file_path)); } TEST_F(NetLogTempFileTest, EnsureInitAllowStartOrSend) { // Create and close an empty log file, to simulate an old log file already // existing. base::ScopedFILE created_file(base::OpenFile(net_export_log_, "w")); ASSERT_TRUE(created_file.get()); created_file.reset(); EXPECT_TRUE(net_log_temp_file_->EnsureInit()); EXPECT_EQ("NOT_LOGGING", GetStateString()); EXPECT_EQ(NetLogTempFile::STATE_NOT_LOGGING, net_log_temp_file_->state()); EXPECT_EQ("UNKNOWN", GetLogTypeString()); EXPECT_EQ(NetLogTempFile::LOG_TYPE_UNKNOWN, net_log_temp_file_->log_type()); EXPECT_EQ(net_export_log_, net_log_temp_file_->log_path_); EXPECT_TRUE(base::PathExists(net_export_log_)); base::FilePath net_export_file_path; EXPECT_TRUE(net_log_temp_file_->GetFilePath(&net_export_file_path)); EXPECT_TRUE(base::PathExists(net_export_file_path)); EXPECT_EQ(net_export_log_, net_export_file_path); } TEST_F(NetLogTempFileTest, ProcessCommandDoStartAndStop) { net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_START); VerifyFileAndStateAfterDoStart(); // Calling a second time should be a no-op. net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_START); VerifyFileAndStateAfterDoStart(); // starting with other log levels should also be no-ops. net_log_temp_file_->ProcessCommand( NetLogTempFile::DO_START_STRIP_PRIVATE_DATA); VerifyFileAndStateAfterDoStart(); net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_START_LOG_BYTES); VerifyFileAndStateAfterDoStart(); net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_STOP); VerifyFileAndStateAfterDoStop(); // Calling DO_STOP second time should be a no-op. net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_STOP); VerifyFileAndStateAfterDoStop(); } TEST_F(NetLogTempFileTest, ProcessCommandDoStartAndStopWithPrivateDataStripping) { net_log_temp_file_->ProcessCommand( NetLogTempFile::DO_START_STRIP_PRIVATE_DATA); VerifyFileAndStateAfterDoStartStripPrivateData(); // Calling a second time should be a no-op. net_log_temp_file_->ProcessCommand( NetLogTempFile::DO_START_STRIP_PRIVATE_DATA); VerifyFileAndStateAfterDoStartStripPrivateData(); net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_STOP); VerifyFileAndStateAfterDoStopWithStripPrivateData(); // Calling DO_STOP second time should be a no-op. net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_STOP); VerifyFileAndStateAfterDoStopWithStripPrivateData(); } TEST_F(NetLogTempFileTest, ProcessCommandDoStartAndStopWithByteLogging) { net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_START_LOG_BYTES); VerifyFileAndStateAfterDoStartLogBytes(); // Calling a second time should be a no-op. net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_START_LOG_BYTES); VerifyFileAndStateAfterDoStartLogBytes(); net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_STOP); VerifyFileAndStateAfterDoStopWithLogBytes(); // Calling DO_STOP second time should be a no-op. net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_STOP); VerifyFileAndStateAfterDoStopWithLogBytes(); } TEST_F(NetLogTempFileTest, DoStartClearsFile) { // Verify file sizes after two consecutive starts/stops are the same (even if // we add some junk data in between). net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_START); VerifyFileAndStateAfterDoStart(); int64 start_file_size; EXPECT_TRUE(base::GetFileSize(net_export_log_, &start_file_size)); net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_STOP); VerifyFileAndStateAfterDoStop(); int64 stop_file_size; EXPECT_TRUE(base::GetFileSize(net_export_log_, &stop_file_size)); EXPECT_GE(stop_file_size, start_file_size); // Add some junk at the end of the file. std::string junk_data("Hello"); EXPECT_TRUE( base::AppendToFile(net_export_log_, junk_data.c_str(), junk_data.size())); int64 junk_file_size; EXPECT_TRUE(base::GetFileSize(net_export_log_, &junk_file_size)); EXPECT_GT(junk_file_size, stop_file_size); // Execute DO_START/DO_STOP commands and make sure the file is back to the // size before addition of junk data. net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_START); VerifyFileAndStateAfterDoStart(); int64 new_start_file_size; EXPECT_TRUE(base::GetFileSize(net_export_log_, &new_start_file_size)); EXPECT_EQ(new_start_file_size, start_file_size); net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_STOP); VerifyFileAndStateAfterDoStop(); int64 new_stop_file_size; EXPECT_TRUE(base::GetFileSize(net_export_log_, &new_stop_file_size)); EXPECT_EQ(new_stop_file_size, stop_file_size); } TEST_F(NetLogTempFileTest, CheckAddEvent) { // Add an event to |net_log_| and then test to make sure that, after we stop // logging, the file is larger than the file created without that event. net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_START); VerifyFileAndStateAfterDoStart(); // Get file size without the event. net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_STOP); VerifyFileAndStateAfterDoStop(); int64 stop_file_size; EXPECT_TRUE(base::GetFileSize(net_export_log_, &stop_file_size)); // Perform DO_START and add an Event and then DO_STOP and then compare // file sizes. net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_START); VerifyFileAndStateAfterDoStart(); // Log an event. net_log_->AddGlobalEntry(net::NetLog::TYPE_CANCELLED); net_log_temp_file_->ProcessCommand(NetLogTempFile::DO_STOP); VerifyFileAndStateAfterDoStop(); int64 new_stop_file_size; EXPECT_TRUE(base::GetFileSize(net_export_log_, &new_stop_file_size)); EXPECT_GE(new_stop_file_size, stop_file_size); } } // namespace net_log