| // Copyright (c) 2012 The Chromium OS 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 "power_manager/powerd/system/async_file_reader.h" |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include <base/bind.h> |
| #include <base/callback.h> |
| #include <base/compiler_specific.h> |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <gtest/gtest.h> |
| |
| #include "power_manager/common/test_main_loop_runner.h" |
| #include "power_manager/common/util.h" |
| |
| namespace power_manager { |
| namespace system { |
| |
| namespace { |
| |
| // Used to construct dummy files. |
| const char kDummyString[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"; |
| |
| // Dummy file name. |
| const char kDummyFileName[] = "dummy_file"; |
| |
| // Creates |path| and writes |total_size| bytes to it. |
| void CreateFile(const base::FilePath& path, size_t total_size) { |
| std::string file_contents; |
| |
| // Add |kDummyString| repeatedly to the file contents. |
| // If total_size % kDummyString.length() > 0, the last instance of |
| // |kDummyString| will only be partially written to it. |
| const std::string dummy_string = kDummyString; |
| size_t remaining_size = total_size; |
| while (remaining_size > 0) { |
| size_t size_to_write = std::min(remaining_size, dummy_string.length()); |
| file_contents += dummy_string.substr(0, size_to_write); |
| remaining_size -= size_to_write; |
| } |
| EXPECT_EQ(total_size, file_contents.length()); |
| |
| // Write the contents to file and make sure the right size was written. |
| EXPECT_EQ(total_size, |
| base::WriteFile(path, file_contents.data(), total_size)); |
| } |
| |
| // When testing a file read where |file size| > |initial read size|, this is the |
| // number of block read iterations required to read the file. |
| // AsyncFileReader doubles the read block size each iteration, so the total file |
| // size it attempts to read (as a multiple of initial read block size) is: |
| // |
| // 1 + 2 + 4 + ... + 2^(N-1) = 2^N - 1 |
| // |
| // where N is the number of reads. |
| int GetMultipleReadFactor(int num_multiple_reads) { |
| return (1 << num_multiple_reads) - 1; |
| } |
| |
| // Maximum time allowed for file read in milliseconds. |
| const int kMaxFileReadTimeMs = 60000; |
| |
| } // namespace |
| |
| class AsyncFileReaderTest : public ::testing::Test { |
| public: |
| AsyncFileReaderTest() |
| : temp_dir_(new base::ScopedTempDir()), |
| file_reader_(new AsyncFileReader()), |
| got_error_(false) { |
| CHECK(temp_dir_->CreateUniqueTempDir()); |
| CHECK(temp_dir_->IsValid()); |
| path_ = temp_dir_->GetPath().Append(kDummyFileName); |
| } |
| ~AsyncFileReaderTest() override {} |
| |
| protected: |
| // Creates a file containing |file_size| bytes and uses AsyncFileReader to |
| // read from it, starting with an |initial_read_size|-byte chunk. Returns |
| // false if initialization failed or if the reader timed out. |
| bool WriteAndReadData(size_t file_size, |
| size_t initial_read_size) WARN_UNUSED_RESULT { |
| data_.clear(); |
| got_error_ = false; |
| |
| CreateFile(path_, file_size); |
| file_reader_->set_initial_read_size_for_testing(initial_read_size); |
| if (!file_reader_->Init(path_)) |
| return false; |
| file_reader_->StartRead( |
| base::Bind(&AsyncFileReaderTest::ReadCallback, base::Unretained(this)), |
| base::Bind(&AsyncFileReaderTest::ErrorCallback, |
| base::Unretained(this))); |
| return loop_runner_.StartLoop( |
| base::TimeDelta::FromMilliseconds(kMaxFileReadTimeMs)); |
| } |
| |
| // Returns the contents of |path_|. |
| std::string ReadFile() { |
| std::string data; |
| CHECK(base::ReadFileToString(path_, &data)); |
| return data; |
| } |
| |
| // Callback for successful reads. |
| void ReadCallback(const std::string& data) { |
| loop_runner_.StopLoop(); |
| data_ = data; |
| } |
| |
| // Callback for read errors. |
| void ErrorCallback() { |
| loop_runner_.StopLoop(); |
| got_error_ = true; |
| } |
| |
| TestMainLoopRunner loop_runner_; |
| |
| std::unique_ptr<base::ScopedTempDir> temp_dir_; |
| std::unique_ptr<AsyncFileReader> file_reader_; |
| |
| // Test file. |
| base::FilePath path_; |
| |
| // Data read from |path_| by |file_reader_|. |
| std::string data_; |
| |
| // True if |file_reader_| reported an error. |
| bool got_error_; |
| }; |
| |
| // Read an empty file. |
| TEST_F(AsyncFileReaderTest, EmptyFileRead) { |
| ASSERT_TRUE(WriteAndReadData(0, 32)); |
| EXPECT_FALSE(got_error_); |
| EXPECT_EQ("", data_); |
| } |
| |
| // Read a file with one block read, with the file size being less than the block |
| // size (partial block read). |
| TEST_F(AsyncFileReaderTest, SingleBlockReadPartial) { |
| ASSERT_TRUE(WriteAndReadData(31, 32)); |
| EXPECT_FALSE(got_error_); |
| EXPECT_EQ(ReadFile(), data_); |
| } |
| |
| // Read a file with one block read, with the file size being equal to the block |
| // size (full block read). |
| TEST_F(AsyncFileReaderTest, SingleBlockReadFull) { |
| ASSERT_TRUE(WriteAndReadData(32, 32)); |
| EXPECT_FALSE(got_error_); |
| EXPECT_EQ(ReadFile(), data_); |
| } |
| |
| // Read a file with multiple block reads, with the last block being a partial |
| // read. |
| TEST_F(AsyncFileReaderTest, MultipleBlockReadPartial) { |
| ASSERT_TRUE(WriteAndReadData(32 * GetMultipleReadFactor(5) - 1, 32)); |
| EXPECT_FALSE(got_error_); |
| EXPECT_EQ(ReadFile(), data_); |
| } |
| |
| // Read a file with multiple block reads, with the last block being a full read. |
| TEST_F(AsyncFileReaderTest, MultipleBlockReadFull) { |
| ASSERT_TRUE(WriteAndReadData(32 * GetMultipleReadFactor(5), 32)); |
| EXPECT_FALSE(got_error_); |
| EXPECT_EQ(ReadFile(), data_); |
| } |
| |
| // Initializing the reader with a nonexistent file should fail. |
| TEST_F(AsyncFileReaderTest, InitWithMissingFile) { |
| EXPECT_FALSE(file_reader_->Init(path_)); |
| } |
| |
| } // namespace system |
| } // namespace power_manager |