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