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