| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/system/diagnostics/async_log.h" |
| |
| #include <memory> |
| |
| #include "ash/system/diagnostics/log_test_helpers.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/run_loop.h" |
| #include "base/test/task_environment.h" |
| #include "base/test/test_simple_task_runner.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace ash { |
| namespace diagnostics { |
| namespace { |
| |
| const char kLogFileName[] = "test_async_log"; |
| |
| } // namespace |
| |
| class AsyncLogTest : public testing::Test { |
| public: |
| AsyncLogTest() : task_runner_(new base::TestSimpleTaskRunner) { |
| EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| log_path_ = temp_dir_.GetPath().AppendASCII(kLogFileName); |
| } |
| |
| ~AsyncLogTest() override { base::RunLoop().RunUntilIdle(); } |
| |
| protected: |
| base::test::TaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner_; |
| |
| base::ScopedTempDir temp_dir_; |
| base::FilePath log_path_; |
| }; |
| |
| TEST_F(AsyncLogTest, NoWriteEmpty) { |
| AsyncLog log(log_path_); |
| log.SetTaskRunnerForTesting(task_runner_); |
| |
| // The file won't until it is written to. |
| EXPECT_FALSE(base::PathExists(log_path_)); |
| |
| // The log is empty. |
| EXPECT_TRUE(log.GetContents().empty()); |
| } |
| |
| TEST_F(AsyncLogTest, WriteEmpty) { |
| AsyncLog log(log_path_); |
| log.SetTaskRunnerForTesting(task_runner_); |
| |
| // Append empty string to the log. |
| log.Append(""); |
| |
| EXPECT_TRUE(task_runner_->HasPendingTask()); |
| // Ensure pending tasks complete. |
| task_runner_->RunUntilIdle(); |
| EXPECT_FALSE(task_runner_->HasPendingTask()); |
| |
| // The file exists. |
| EXPECT_TRUE(base::PathExists(log_path_)); |
| |
| // But log is still empty. |
| EXPECT_TRUE(log.GetContents().empty()); |
| } |
| |
| TEST_F(AsyncLogTest, WriteOneLine) { |
| AsyncLog log(log_path_); |
| log.SetTaskRunnerForTesting(task_runner_); |
| |
| const std::string line = "Hello"; |
| |
| // Append `line` to the log. |
| log.Append(line); |
| |
| // Ensure pending tasks complete. |
| task_runner_->RunUntilIdle(); |
| |
| // Log contains `line`. |
| EXPECT_EQ(line, log.GetContents()); |
| } |
| |
| TEST_F(AsyncLogTest, WriteMultipleLines) { |
| AsyncLog log(log_path_); |
| log.SetTaskRunnerForTesting(task_runner_); |
| |
| const std::vector<std::string> lines = { |
| "Line 1", |
| "Line 2", |
| "Line 3", |
| }; |
| |
| // Append all the `lines` with a new line to the log. |
| for (auto line : lines) { |
| log.Append(line + "\n"); |
| } |
| |
| // Ensure pending tasks complete. |
| task_runner_->RunUntilIdle(); |
| |
| // Read back the log and split the lines. |
| EXPECT_EQ(lines, GetLogLines(log.GetContents())); |
| } |
| |
| TEST_F(AsyncLogTest, NoUseAfterFreeCrash) { |
| const std::string new_line = "Line\n"; |
| |
| // Simulate race conditions between the destruction of AsyncLog and the |
| // execution of AppendImpl. |
| for (size_t i = 0; i < 10; ++i) { |
| auto log = std::make_unique<AsyncLog>(log_path_); |
| log->Append(new_line); |
| } |
| |
| // This should finish without crash. |
| task_environment_.RunUntilIdle(); |
| } |
| |
| } // namespace diagnostics |
| } // namespace ash |