• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "rtc_base/file_rotating_stream.h"
12 
13 #include <string.h>
14 
15 #include <cstdint>
16 #include <memory>
17 
18 #include "rtc_base/arraysize.h"
19 #include "test/gtest.h"
20 #include "test/testsupport/file_utils.h"
21 
22 namespace rtc {
23 
24 namespace {
25 
CleanupLogDirectory(const FileRotatingStream & stream)26 void CleanupLogDirectory(const FileRotatingStream& stream) {
27   for (size_t i = 0; i < stream.GetNumFiles(); ++i) {
28     // Ignore return value, not all files are expected to exist.
29     webrtc::test::RemoveFile(stream.GetFilePath(i));
30   }
31 }
32 
33 }  // namespace
34 
35 #if defined(WEBRTC_ANDROID)
36 // Fails on Android: https://bugs.chromium.org/p/webrtc/issues/detail?id=4364.
37 #define MAYBE_FileRotatingStreamTest DISABLED_FileRotatingStreamTest
38 #else
39 #define MAYBE_FileRotatingStreamTest FileRotatingStreamTest
40 #endif
41 
42 class MAYBE_FileRotatingStreamTest : public ::testing::Test {
43  protected:
44   static const char* kFilePrefix;
45   static const size_t kMaxFileSize;
46 
Init(const std::string & dir_name,const std::string & file_prefix,size_t max_file_size,size_t num_log_files,bool ensure_trailing_delimiter=true)47   void Init(const std::string& dir_name,
48             const std::string& file_prefix,
49             size_t max_file_size,
50             size_t num_log_files,
51             bool ensure_trailing_delimiter = true) {
52     dir_path_ = webrtc::test::OutputPath();
53 
54     // Append per-test output path in order to run within gtest parallel.
55     dir_path_.append(dir_name);
56     if (ensure_trailing_delimiter) {
57       dir_path_.append(webrtc::test::kPathDelimiter);
58     }
59     ASSERT_TRUE(webrtc::test::CreateDir(dir_path_));
60     stream_.reset(new FileRotatingStream(dir_path_, file_prefix, max_file_size,
61                                          num_log_files));
62   }
63 
TearDown()64   void TearDown() override {
65     // On windows, open files can't be removed.
66     stream_->Close();
67     CleanupLogDirectory(*stream_);
68     EXPECT_TRUE(webrtc::test::RemoveDir(dir_path_));
69 
70     stream_.reset();
71   }
72 
73   // Writes the data to the stream and flushes it.
WriteAndFlush(const void * data,const size_t data_len)74   void WriteAndFlush(const void* data, const size_t data_len) {
75     EXPECT_EQ(SR_SUCCESS, stream_->WriteAll(data, data_len, nullptr, nullptr));
76     EXPECT_TRUE(stream_->Flush());
77   }
78 
79   // Checks that the stream reads in the expected contents and then returns an
80   // end of stream result.
VerifyStreamRead(const char * expected_contents,const size_t expected_length,const std::string & dir_path,const char * file_prefix)81   void VerifyStreamRead(const char* expected_contents,
82                         const size_t expected_length,
83                         const std::string& dir_path,
84                         const char* file_prefix) {
85     FileRotatingStreamReader reader(dir_path, file_prefix);
86     EXPECT_EQ(reader.GetSize(), expected_length);
87     std::unique_ptr<uint8_t[]> buffer(new uint8_t[expected_length]);
88     memset(buffer.get(), 0, expected_length);
89     EXPECT_EQ(expected_length, reader.ReadAll(buffer.get(), expected_length));
90     EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
91   }
92 
VerifyFileContents(const char * expected_contents,const size_t expected_length,const std::string & file_path)93   void VerifyFileContents(const char* expected_contents,
94                           const size_t expected_length,
95                           const std::string& file_path) {
96     std::unique_ptr<uint8_t[]> buffer(new uint8_t[expected_length + 1]);
97     webrtc::FileWrapper f = webrtc::FileWrapper::OpenReadOnly(file_path);
98     ASSERT_TRUE(f.is_open());
99     size_t size_read = f.Read(buffer.get(), expected_length + 1);
100     EXPECT_EQ(size_read, expected_length);
101     EXPECT_EQ(0, memcmp(expected_contents, buffer.get(),
102                         std::min(expected_length, size_read)));
103   }
104 
105   std::unique_ptr<FileRotatingStream> stream_;
106   std::string dir_path_;
107 };
108 
109 const char* MAYBE_FileRotatingStreamTest::kFilePrefix =
110     "FileRotatingStreamTest";
111 const size_t MAYBE_FileRotatingStreamTest::kMaxFileSize = 2;
112 
113 // Tests that stream state is correct before and after Open / Close.
TEST_F(MAYBE_FileRotatingStreamTest,State)114 TEST_F(MAYBE_FileRotatingStreamTest, State) {
115   Init("FileRotatingStreamTestState", kFilePrefix, kMaxFileSize, 3);
116 
117   EXPECT_EQ(SS_CLOSED, stream_->GetState());
118   ASSERT_TRUE(stream_->Open());
119   EXPECT_EQ(SS_OPEN, stream_->GetState());
120   stream_->Close();
121   EXPECT_EQ(SS_CLOSED, stream_->GetState());
122 }
123 
124 // Tests that nothing is written to file when data of length zero is written.
TEST_F(MAYBE_FileRotatingStreamTest,EmptyWrite)125 TEST_F(MAYBE_FileRotatingStreamTest, EmptyWrite) {
126   Init("FileRotatingStreamTestEmptyWrite", kFilePrefix, kMaxFileSize, 3);
127 
128   ASSERT_TRUE(stream_->Open());
129   WriteAndFlush("a", 0);
130 
131   std::string logfile_path = stream_->GetFilePath(0);
132   webrtc::FileWrapper f = webrtc::FileWrapper::OpenReadOnly(logfile_path);
133   ASSERT_TRUE(f.is_open());
134   char buf[1];
135   EXPECT_EQ(0u, f.Read(buf, sizeof(buf)));
136 }
137 
138 // Tests that a write operation followed by a read returns the expected data
139 // and writes to the expected files.
TEST_F(MAYBE_FileRotatingStreamTest,WriteAndRead)140 TEST_F(MAYBE_FileRotatingStreamTest, WriteAndRead) {
141   Init("FileRotatingStreamTestWriteAndRead", kFilePrefix, kMaxFileSize, 3);
142 
143   ASSERT_TRUE(stream_->Open());
144   // The test is set up to create three log files of length 2. Write and check
145   // contents.
146   std::string messages[3] = {"aa", "bb", "cc"};
147   for (size_t i = 0; i < arraysize(messages); ++i) {
148     const std::string& message = messages[i];
149     WriteAndFlush(message.c_str(), message.size());
150     // Since the max log size is 2, we will be causing rotation. Read from the
151     // next file.
152     VerifyFileContents(message.c_str(), message.size(),
153                        stream_->GetFilePath(1));
154   }
155   // Check that exactly three files exist.
156   for (size_t i = 0; i < arraysize(messages); ++i) {
157     EXPECT_TRUE(webrtc::test::FileExists(stream_->GetFilePath(i)));
158   }
159   std::string message("d");
160   WriteAndFlush(message.c_str(), message.size());
161   for (size_t i = 0; i < arraysize(messages); ++i) {
162     EXPECT_TRUE(webrtc::test::FileExists(stream_->GetFilePath(i)));
163   }
164   // TODO(tkchin): Maybe check all the files in the dir.
165 
166   // Reopen for read.
167   std::string expected_contents("bbccd");
168   VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
169                    dir_path_, kFilePrefix);
170 }
171 
172 // Tests that a write operation (with dir name without delimiter) followed by a
173 // read returns the expected data and writes to the expected files.
TEST_F(MAYBE_FileRotatingStreamTest,WriteWithoutDelimiterAndRead)174 TEST_F(MAYBE_FileRotatingStreamTest, WriteWithoutDelimiterAndRead) {
175   Init("FileRotatingStreamTestWriteWithoutDelimiterAndRead", kFilePrefix,
176        kMaxFileSize, 3,
177        /* ensure_trailing_delimiter*/ false);
178 
179   ASSERT_TRUE(stream_->Open());
180   // The test is set up to create three log files of length 2. Write and check
181   // contents.
182   std::string messages[3] = {"aa", "bb", "cc"};
183   for (size_t i = 0; i < arraysize(messages); ++i) {
184     const std::string& message = messages[i];
185     WriteAndFlush(message.c_str(), message.size());
186   }
187   std::string message("d");
188   WriteAndFlush(message.c_str(), message.size());
189 
190   // Reopen for read.
191   std::string expected_contents("bbccd");
192   VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
193                    dir_path_ + webrtc::test::kPathDelimiter, kFilePrefix);
194 }
195 
196 // Tests that a write operation followed by a read (without trailing delimiter)
197 // returns the expected data and writes to the expected files.
TEST_F(MAYBE_FileRotatingStreamTest,WriteAndReadWithoutDelimiter)198 TEST_F(MAYBE_FileRotatingStreamTest, WriteAndReadWithoutDelimiter) {
199   Init("FileRotatingStreamTestWriteAndReadWithoutDelimiter", kFilePrefix,
200        kMaxFileSize, 3);
201 
202   ASSERT_TRUE(stream_->Open());
203   // The test is set up to create three log files of length 2. Write and check
204   // contents.
205   std::string messages[3] = {"aa", "bb", "cc"};
206   for (size_t i = 0; i < arraysize(messages); ++i) {
207     const std::string& message = messages[i];
208     WriteAndFlush(message.c_str(), message.size());
209   }
210   std::string message("d");
211   WriteAndFlush(message.c_str(), message.size());
212 
213   // Reopen for read.
214   std::string expected_contents("bbccd");
215   VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
216                    dir_path_.substr(0, dir_path_.size() - 1), kFilePrefix);
217 }
218 
219 // Tests that writing data greater than the total capacity of the files
220 // overwrites the files correctly and is read correctly after.
TEST_F(MAYBE_FileRotatingStreamTest,WriteOverflowAndRead)221 TEST_F(MAYBE_FileRotatingStreamTest, WriteOverflowAndRead) {
222   Init("FileRotatingStreamTestWriteOverflowAndRead", kFilePrefix, kMaxFileSize,
223        3);
224   ASSERT_TRUE(stream_->Open());
225   // This should cause overflow across all three files, such that the first file
226   // we wrote to also gets overwritten.
227   std::string message("foobarbaz");
228   WriteAndFlush(message.c_str(), message.size());
229   std::string expected_file_contents("z");
230   VerifyFileContents(expected_file_contents.c_str(),
231                      expected_file_contents.size(), stream_->GetFilePath(0));
232   std::string expected_stream_contents("arbaz");
233   VerifyStreamRead(expected_stream_contents.c_str(),
234                    expected_stream_contents.size(), dir_path_, kFilePrefix);
235 }
236 
237 // Tests that the returned file paths have the right folder and prefix.
TEST_F(MAYBE_FileRotatingStreamTest,GetFilePath)238 TEST_F(MAYBE_FileRotatingStreamTest, GetFilePath) {
239   Init("FileRotatingStreamTestGetFilePath", kFilePrefix, kMaxFileSize, 20);
240   // dir_path_ includes a trailing delimiter.
241   const std::string prefix = dir_path_ + kFilePrefix;
242   for (auto i = 0; i < 20; ++i) {
243     EXPECT_EQ(0, stream_->GetFilePath(i).compare(0, prefix.size(), prefix));
244   }
245 }
246 
247 #if defined(WEBRTC_ANDROID)
248 // Fails on Android: https://bugs.chromium.org/p/webrtc/issues/detail?id=4364.
249 #define MAYBE_CallSessionFileRotatingStreamTest \
250   DISABLED_CallSessionFileRotatingStreamTest
251 #else
252 #define MAYBE_CallSessionFileRotatingStreamTest \
253   CallSessionFileRotatingStreamTest
254 #endif
255 
256 class MAYBE_CallSessionFileRotatingStreamTest : public ::testing::Test {
257  protected:
Init(const std::string & dir_name,size_t max_total_log_size)258   void Init(const std::string& dir_name, size_t max_total_log_size) {
259     dir_path_ = webrtc::test::OutputPath();
260 
261     // Append per-test output path in order to run within gtest parallel.
262     dir_path_.append(dir_name);
263     dir_path_.append(webrtc::test::kPathDelimiter);
264     ASSERT_TRUE(webrtc::test::CreateDir(dir_path_));
265     stream_.reset(
266         new CallSessionFileRotatingStream(dir_path_, max_total_log_size));
267   }
268 
TearDown()269   void TearDown() override {
270     // On windows, open files can't be removed.
271     stream_->Close();
272     CleanupLogDirectory(*stream_);
273     EXPECT_TRUE(webrtc::test::RemoveDir(dir_path_));
274 
275     stream_.reset();
276   }
277 
278   // Writes the data to the stream and flushes it.
WriteAndFlush(const void * data,const size_t data_len)279   void WriteAndFlush(const void* data, const size_t data_len) {
280     EXPECT_EQ(SR_SUCCESS, stream_->WriteAll(data, data_len, nullptr, nullptr));
281     EXPECT_TRUE(stream_->Flush());
282   }
283 
284   // Checks that the stream reads in the expected contents and then returns an
285   // end of stream result.
VerifyStreamRead(const char * expected_contents,const size_t expected_length,const std::string & dir_path)286   void VerifyStreamRead(const char* expected_contents,
287                         const size_t expected_length,
288                         const std::string& dir_path) {
289     CallSessionFileRotatingStreamReader reader(dir_path);
290     EXPECT_EQ(reader.GetSize(), expected_length);
291     std::unique_ptr<uint8_t[]> buffer(new uint8_t[expected_length]);
292     memset(buffer.get(), 0, expected_length);
293     EXPECT_EQ(expected_length, reader.ReadAll(buffer.get(), expected_length));
294     EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
295   }
296 
297   std::unique_ptr<CallSessionFileRotatingStream> stream_;
298   std::string dir_path_;
299 };
300 
301 // Tests that writing and reading to a stream with the smallest possible
302 // capacity works.
TEST_F(MAYBE_CallSessionFileRotatingStreamTest,WriteAndReadSmallest)303 TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadSmallest) {
304   Init("CallSessionFileRotatingStreamTestWriteAndReadSmallest", 4);
305 
306   ASSERT_TRUE(stream_->Open());
307   std::string message("abcde");
308   WriteAndFlush(message.c_str(), message.size());
309   std::string expected_contents("abe");
310   VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
311                    dir_path_);
312 }
313 
314 // Tests that writing and reading to a stream with capacity lesser than 4MB
315 // behaves correctly.
TEST_F(MAYBE_CallSessionFileRotatingStreamTest,WriteAndReadSmall)316 TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadSmall) {
317   Init("CallSessionFileRotatingStreamTestWriteAndReadSmall", 8);
318 
319   ASSERT_TRUE(stream_->Open());
320   std::string message("123456789");
321   WriteAndFlush(message.c_str(), message.size());
322   std::string expected_contents("1234789");
323   VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
324                    dir_path_);
325 }
326 
327 // Tests that writing and reading to a stream with capacity greater than 4MB
328 // behaves correctly.
TEST_F(MAYBE_CallSessionFileRotatingStreamTest,WriteAndReadLarge)329 TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadLarge) {
330   Init("CallSessionFileRotatingStreamTestWriteAndReadLarge", 6 * 1024 * 1024);
331 
332   ASSERT_TRUE(stream_->Open());
333   const size_t buffer_size = 1024 * 1024;
334   std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
335   for (int i = 0; i < 8; i++) {
336     memset(buffer.get(), i, buffer_size);
337     EXPECT_EQ(SR_SUCCESS,
338               stream_->WriteAll(buffer.get(), buffer_size, nullptr, nullptr));
339   }
340 
341   const int expected_vals[] = {0, 1, 2, 6, 7};
342   const size_t expected_size = buffer_size * arraysize(expected_vals);
343 
344   CallSessionFileRotatingStreamReader reader(dir_path_);
345   EXPECT_EQ(reader.GetSize(), expected_size);
346   std::unique_ptr<uint8_t[]> contents(new uint8_t[expected_size + 1]);
347   EXPECT_EQ(reader.ReadAll(contents.get(), expected_size + 1), expected_size);
348   for (size_t i = 0; i < arraysize(expected_vals); ++i) {
349     const uint8_t* block = contents.get() + i * buffer_size;
350     bool match = true;
351     for (size_t j = 0; j < buffer_size; j++) {
352       if (block[j] != expected_vals[i]) {
353         match = false;
354         break;
355       }
356     }
357     // EXPECT call at end of loop, to limit the number of messages on failure.
358     EXPECT_TRUE(match);
359   }
360 }
361 
362 // Tests that writing and reading to a stream where only the first file is
363 // written to behaves correctly.
TEST_F(MAYBE_CallSessionFileRotatingStreamTest,WriteAndReadFirstHalf)364 TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadFirstHalf) {
365   Init("CallSessionFileRotatingStreamTestWriteAndReadFirstHalf",
366        6 * 1024 * 1024);
367   ASSERT_TRUE(stream_->Open());
368   const size_t buffer_size = 1024 * 1024;
369   std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
370   for (int i = 0; i < 2; i++) {
371     memset(buffer.get(), i, buffer_size);
372     EXPECT_EQ(SR_SUCCESS,
373               stream_->WriteAll(buffer.get(), buffer_size, nullptr, nullptr));
374   }
375 
376   const int expected_vals[] = {0, 1};
377   const size_t expected_size = buffer_size * arraysize(expected_vals);
378 
379   CallSessionFileRotatingStreamReader reader(dir_path_);
380   EXPECT_EQ(reader.GetSize(), expected_size);
381   std::unique_ptr<uint8_t[]> contents(new uint8_t[expected_size + 1]);
382   EXPECT_EQ(reader.ReadAll(contents.get(), expected_size + 1), expected_size);
383 
384   for (size_t i = 0; i < arraysize(expected_vals); ++i) {
385     const uint8_t* block = contents.get() + i * buffer_size;
386     bool match = true;
387     for (size_t j = 0; j < buffer_size; j++) {
388       if (block[j] != expected_vals[i]) {
389         match = false;
390         break;
391       }
392     }
393     // EXPECT call at end of loop, to limit the number of messages on failure.
394     EXPECT_TRUE(match);
395   }
396 }
397 
398 }  // namespace rtc
399