Home | History | Annotate | Download | only in base
      1 /*
      2  *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/base/arraysize.h"
     12 #include "webrtc/base/checks.h"
     13 #include "webrtc/base/filerotatingstream.h"
     14 #include "webrtc/base/fileutils.h"
     15 #include "webrtc/base/gunit.h"
     16 #include "webrtc/base/pathutils.h"
     17 
     18 namespace rtc {
     19 
     20 class FileRotatingStreamTest : public ::testing::Test {
     21  protected:
     22   static const char* kFilePrefix;
     23   static const size_t kMaxFileSize;
     24 
     25   void Init(const std::string& dir_name,
     26             const std::string& file_prefix,
     27             size_t max_file_size,
     28             size_t num_log_files) {
     29     Pathname test_path;
     30     ASSERT_TRUE(Filesystem::GetAppTempFolder(&test_path));
     31     // Append per-test output path in order to run within gtest parallel.
     32     test_path.AppendFolder(dir_name);
     33     ASSERT_TRUE(Filesystem::CreateFolder(test_path));
     34     dir_path_ = test_path.pathname();
     35     ASSERT_TRUE(dir_path_.size());
     36     stream_.reset(new FileRotatingStream(dir_path_, file_prefix, max_file_size,
     37                                          num_log_files));
     38   }
     39 
     40   void TearDown() override {
     41     stream_.reset();
     42     if (dir_path_.size() && Filesystem::IsFolder(dir_path_) &&
     43         Filesystem::IsTemporaryPath(dir_path_)) {
     44       Filesystem::DeleteFolderAndContents(dir_path_);
     45     }
     46   }
     47 
     48   // Writes the data to the stream and flushes it.
     49   void WriteAndFlush(const void* data, const size_t data_len) {
     50     EXPECT_EQ(SR_SUCCESS, stream_->WriteAll(data, data_len, nullptr, nullptr));
     51     EXPECT_TRUE(stream_->Flush());
     52   }
     53 
     54   // Checks that the stream reads in the expected contents and then returns an
     55   // end of stream result.
     56   void VerifyStreamRead(const char* expected_contents,
     57                         const size_t expected_length,
     58                         const std::string& dir_path,
     59                         const char* file_prefix) {
     60     scoped_ptr<FileRotatingStream> stream;
     61     stream.reset(new FileRotatingStream(dir_path, file_prefix));
     62     ASSERT_TRUE(stream->Open());
     63     size_t read = 0;
     64     size_t stream_size = 0;
     65     EXPECT_TRUE(stream->GetSize(&stream_size));
     66     scoped_ptr<uint8_t[]> buffer(new uint8_t[expected_length]);
     67     EXPECT_EQ(SR_SUCCESS,
     68               stream->ReadAll(buffer.get(), expected_length, &read, nullptr));
     69     EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
     70     EXPECT_EQ(SR_EOS, stream->ReadAll(buffer.get(), 1, nullptr, nullptr));
     71     EXPECT_EQ(stream_size, read);
     72   }
     73 
     74   void VerifyFileContents(const char* expected_contents,
     75                           const size_t expected_length,
     76                           const std::string& file_path) {
     77     scoped_ptr<uint8_t[]> buffer(new uint8_t[expected_length]);
     78     scoped_ptr<FileStream> stream(Filesystem::OpenFile(file_path, "r"));
     79     EXPECT_TRUE(stream);
     80     if (!stream) {
     81       return;
     82     }
     83     EXPECT_EQ(rtc::SR_SUCCESS,
     84               stream->ReadAll(buffer.get(), expected_length, nullptr, nullptr));
     85     EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
     86     size_t file_size = 0;
     87     EXPECT_TRUE(stream->GetSize(&file_size));
     88     EXPECT_EQ(file_size, expected_length);
     89   }
     90 
     91   scoped_ptr<FileRotatingStream> stream_;
     92   std::string dir_path_;
     93 };
     94 
     95 const char* FileRotatingStreamTest::kFilePrefix = "FileRotatingStreamTest";
     96 const size_t FileRotatingStreamTest::kMaxFileSize = 2;
     97 
     98 // Tests that stream state is correct before and after Open / Close.
     99 TEST_F(FileRotatingStreamTest, State) {
    100   Init("FileRotatingStreamTestState", kFilePrefix, kMaxFileSize, 3);
    101 
    102   EXPECT_EQ(SS_CLOSED, stream_->GetState());
    103   ASSERT_TRUE(stream_->Open());
    104   EXPECT_EQ(SS_OPEN, stream_->GetState());
    105   stream_->Close();
    106   EXPECT_EQ(SS_CLOSED, stream_->GetState());
    107 }
    108 
    109 // Tests that nothing is written to file when data of length zero is written.
    110 TEST_F(FileRotatingStreamTest, EmptyWrite) {
    111   Init("FileRotatingStreamTestEmptyWrite", kFilePrefix, kMaxFileSize, 3);
    112 
    113   ASSERT_TRUE(stream_->Open());
    114   WriteAndFlush("a", 0);
    115 
    116   std::string logfile_path = stream_->GetFilePath(0);
    117   scoped_ptr<FileStream> stream(Filesystem::OpenFile(logfile_path, "r"));
    118   size_t file_size = 0;
    119   EXPECT_TRUE(stream->GetSize(&file_size));
    120   EXPECT_EQ(0u, file_size);
    121 }
    122 
    123 // Tests that a write operation followed by a read returns the expected data
    124 // and writes to the expected files.
    125 TEST_F(FileRotatingStreamTest, WriteAndRead) {
    126   Init("FileRotatingStreamTestWriteAndRead", kFilePrefix, kMaxFileSize, 3);
    127 
    128   ASSERT_TRUE(stream_->Open());
    129   // The test is set up to create three log files of length 2. Write and check
    130   // contents.
    131   std::string messages[3] = {"aa", "bb", "cc"};
    132   for (size_t i = 0; i < arraysize(messages); ++i) {
    133     const std::string& message = messages[i];
    134     WriteAndFlush(message.c_str(), message.size());
    135     // Since the max log size is 2, we will be causing rotation. Read from the
    136     // next file.
    137     VerifyFileContents(message.c_str(), message.size(),
    138                        stream_->GetFilePath(1));
    139   }
    140   // Check that exactly three files exist.
    141   for (size_t i = 0; i < arraysize(messages); ++i) {
    142     EXPECT_TRUE(Filesystem::IsFile(stream_->GetFilePath(i)));
    143   }
    144   std::string message("d");
    145   WriteAndFlush(message.c_str(), message.size());
    146   for (size_t i = 0; i < arraysize(messages); ++i) {
    147     EXPECT_TRUE(Filesystem::IsFile(stream_->GetFilePath(i)));
    148   }
    149   // TODO(tkchin): Maybe check all the files in the dir.
    150 
    151   // Reopen for read.
    152   std::string expected_contents("bbccd");
    153   VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
    154                    dir_path_, kFilePrefix);
    155 }
    156 
    157 // Tests that writing data greater than the total capacity of the files
    158 // overwrites the files correctly and is read correctly after.
    159 TEST_F(FileRotatingStreamTest, WriteOverflowAndRead) {
    160   Init("FileRotatingStreamTestWriteOverflowAndRead", kFilePrefix, kMaxFileSize,
    161        3);
    162   ASSERT_TRUE(stream_->Open());
    163   // This should cause overflow across all three files, such that the first file
    164   // we wrote to also gets overwritten.
    165   std::string message("foobarbaz");
    166   WriteAndFlush(message.c_str(), message.size());
    167   std::string expected_file_contents("z");
    168   VerifyFileContents(expected_file_contents.c_str(),
    169                      expected_file_contents.size(), stream_->GetFilePath(0));
    170   std::string expected_stream_contents("arbaz");
    171   VerifyStreamRead(expected_stream_contents.c_str(),
    172                    expected_stream_contents.size(), dir_path_, kFilePrefix);
    173 }
    174 
    175 // Tests that the returned file paths have the right folder and prefix.
    176 TEST_F(FileRotatingStreamTest, GetFilePath) {
    177   Init("FileRotatingStreamTestGetFilePath", kFilePrefix, kMaxFileSize, 20);
    178   for (auto i = 0; i < 20; ++i) {
    179     Pathname path(stream_->GetFilePath(i));
    180     EXPECT_EQ(0, path.folder().compare(dir_path_));
    181     EXPECT_EQ(0, path.filename().compare(0, strlen(kFilePrefix), kFilePrefix));
    182   }
    183 }
    184 
    185 class CallSessionFileRotatingStreamTest : public ::testing::Test {
    186  protected:
    187   void Init(const std::string& dir_name, size_t max_total_log_size) {
    188     Pathname test_path;
    189     ASSERT_TRUE(Filesystem::GetAppTempFolder(&test_path));
    190     // Append per-test output path in order to run within gtest parallel.
    191     test_path.AppendFolder(dir_name);
    192     ASSERT_TRUE(Filesystem::CreateFolder(test_path));
    193     dir_path_ = test_path.pathname();
    194     ASSERT_TRUE(dir_path_.size());
    195     stream_.reset(
    196         new CallSessionFileRotatingStream(dir_path_, max_total_log_size));
    197   }
    198 
    199   virtual void TearDown() {
    200     stream_.reset();
    201     if (dir_path_.size() && Filesystem::IsFolder(dir_path_) &&
    202         Filesystem::IsTemporaryPath(dir_path_)) {
    203       Filesystem::DeleteFolderAndContents(dir_path_);
    204     }
    205   }
    206 
    207   // Writes the data to the stream and flushes it.
    208   void WriteAndFlush(const void* data, const size_t data_len) {
    209     EXPECT_EQ(SR_SUCCESS, stream_->WriteAll(data, data_len, nullptr, nullptr));
    210     EXPECT_TRUE(stream_->Flush());
    211   }
    212 
    213   // Checks that the stream reads in the expected contents and then returns an
    214   // end of stream result.
    215   void VerifyStreamRead(const char* expected_contents,
    216                         const size_t expected_length,
    217                         const std::string& dir_path) {
    218     scoped_ptr<CallSessionFileRotatingStream> stream(
    219         new CallSessionFileRotatingStream(dir_path));
    220     ASSERT_TRUE(stream->Open());
    221     size_t read = 0;
    222     size_t stream_size = 0;
    223     EXPECT_TRUE(stream->GetSize(&stream_size));
    224     scoped_ptr<uint8_t[]> buffer(new uint8_t[expected_length]);
    225     EXPECT_EQ(SR_SUCCESS,
    226               stream->ReadAll(buffer.get(), expected_length, &read, nullptr));
    227     EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
    228     EXPECT_EQ(SR_EOS, stream->ReadAll(buffer.get(), 1, nullptr, nullptr));
    229     EXPECT_EQ(stream_size, read);
    230   }
    231 
    232   scoped_ptr<CallSessionFileRotatingStream> stream_;
    233   std::string dir_path_;
    234 };
    235 
    236 // Tests that writing and reading to a stream with the smallest possible
    237 // capacity works.
    238 TEST_F(CallSessionFileRotatingStreamTest, WriteAndReadSmallest) {
    239   Init("CallSessionFileRotatingStreamTestWriteAndReadSmallest", 4);
    240 
    241   ASSERT_TRUE(stream_->Open());
    242   std::string message("abcde");
    243   WriteAndFlush(message.c_str(), message.size());
    244   std::string expected_contents("abe");
    245   VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
    246                    dir_path_);
    247 }
    248 
    249 // Tests that writing and reading to a stream with capacity lesser than 4MB
    250 // behaves correctly.
    251 TEST_F(CallSessionFileRotatingStreamTest, WriteAndReadSmall) {
    252   Init("CallSessionFileRotatingStreamTestWriteAndReadSmall", 8);
    253 
    254   ASSERT_TRUE(stream_->Open());
    255   std::string message("123456789");
    256   WriteAndFlush(message.c_str(), message.size());
    257   std::string expected_contents("1234789");
    258   VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
    259                    dir_path_);
    260 }
    261 
    262 // Tests that writing and reading to a stream with capacity greater than 4MB
    263 // behaves correctly.
    264 TEST_F(CallSessionFileRotatingStreamTest, WriteAndReadLarge) {
    265   Init("CallSessionFileRotatingStreamTestWriteAndReadLarge", 6 * 1024 * 1024);
    266 
    267   ASSERT_TRUE(stream_->Open());
    268   const size_t buffer_size = 1024 * 1024;
    269   scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
    270   for (int i = 0; i < 8; i++) {
    271     memset(buffer.get(), i, buffer_size);
    272     EXPECT_EQ(SR_SUCCESS,
    273               stream_->WriteAll(buffer.get(), buffer_size, nullptr, nullptr));
    274   }
    275 
    276   stream_.reset(new CallSessionFileRotatingStream(dir_path_));
    277   ASSERT_TRUE(stream_->Open());
    278   scoped_ptr<uint8_t[]> expected_buffer(new uint8_t[buffer_size]);
    279   int expected_vals[] = {0, 1, 2, 6, 7};
    280   for (size_t i = 0; i < arraysize(expected_vals); ++i) {
    281     memset(expected_buffer.get(), expected_vals[i], buffer_size);
    282     EXPECT_EQ(SR_SUCCESS,
    283               stream_->ReadAll(buffer.get(), buffer_size, nullptr, nullptr));
    284     EXPECT_EQ(0, memcmp(buffer.get(), expected_buffer.get(), buffer_size));
    285   }
    286   EXPECT_EQ(SR_EOS, stream_->ReadAll(buffer.get(), 1, nullptr, nullptr));
    287 }
    288 
    289 // Tests that writing and reading to a stream where only the first file is
    290 // written to behaves correctly.
    291 TEST_F(CallSessionFileRotatingStreamTest, WriteAndReadFirstHalf) {
    292   Init("CallSessionFileRotatingStreamTestWriteAndReadFirstHalf",
    293        6 * 1024 * 1024);
    294   ASSERT_TRUE(stream_->Open());
    295   const size_t buffer_size = 1024 * 1024;
    296   scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
    297   for (int i = 0; i < 2; i++) {
    298     memset(buffer.get(), i, buffer_size);
    299     EXPECT_EQ(SR_SUCCESS,
    300               stream_->WriteAll(buffer.get(), buffer_size, nullptr, nullptr));
    301   }
    302 
    303   stream_.reset(new CallSessionFileRotatingStream(dir_path_));
    304   ASSERT_TRUE(stream_->Open());
    305   scoped_ptr<uint8_t[]> expected_buffer(new uint8_t[buffer_size]);
    306   int expected_vals[] = {0, 1};
    307   for (size_t i = 0; i < arraysize(expected_vals); ++i) {
    308     memset(expected_buffer.get(), expected_vals[i], buffer_size);
    309     EXPECT_EQ(SR_SUCCESS,
    310               stream_->ReadAll(buffer.get(), buffer_size, nullptr, nullptr));
    311     EXPECT_EQ(0, memcmp(buffer.get(), expected_buffer.get(), buffer_size));
    312   }
    313   EXPECT_EQ(SR_EOS, stream_->ReadAll(buffer.get(), 1, nullptr, nullptr));
    314 }
    315 
    316 }  // namespace rtc
    317