blob: 09438f870eec034df71b6c84f2ed261e50b31d5c [file] [log] [blame]
tkchin93411912015-07-22 12:12:17 -07001/*
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
18namespace rtc {
19
20class 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());
tkchin28bae022015-07-23 12:27:02 -070063 size_t read = 0;
64 size_t stream_size = 0;
65 EXPECT_TRUE(stream->GetSize(&stream_size));
tkchin93411912015-07-22 12:12:17 -070066 scoped_ptr<uint8_t[]> buffer(new uint8_t[expected_length]);
67 EXPECT_EQ(SR_SUCCESS,
tkchin28bae022015-07-23 12:27:02 -070068 stream->ReadAll(buffer.get(), expected_length, &read, nullptr));
tkchin93411912015-07-22 12:12:17 -070069 EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
70 EXPECT_EQ(SR_EOS, stream->ReadAll(buffer.get(), 1, nullptr, nullptr));
tkchin28bae022015-07-23 12:27:02 -070071 EXPECT_EQ(stream_size, read);
tkchin93411912015-07-22 12:12:17 -070072 }
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
95const char* FileRotatingStreamTest::kFilePrefix = "FileRotatingStreamTest";
96const size_t FileRotatingStreamTest::kMaxFileSize = 2;
97
98// Tests that stream state is correct before and after Open / Close.
99TEST_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.
110TEST_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.
125TEST_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.
159TEST_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.
176TEST_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
185class 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());
tkchin28bae022015-07-23 12:27:02 -0700221 size_t read = 0;
222 size_t stream_size = 0;
223 EXPECT_TRUE(stream->GetSize(&stream_size));
tkchin93411912015-07-22 12:12:17 -0700224 scoped_ptr<uint8_t[]> buffer(new uint8_t[expected_length]);
225 EXPECT_EQ(SR_SUCCESS,
tkchin28bae022015-07-23 12:27:02 -0700226 stream->ReadAll(buffer.get(), expected_length, &read, nullptr));
tkchin93411912015-07-22 12:12:17 -0700227 EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
228 EXPECT_EQ(SR_EOS, stream->ReadAll(buffer.get(), 1, nullptr, nullptr));
tkchin28bae022015-07-23 12:27:02 -0700229 EXPECT_EQ(stream_size, read);
tkchin93411912015-07-22 12:12:17 -0700230 }
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.
238TEST_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.
251TEST_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.
264TEST_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.
291TEST_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