1 /*
2  *  Copyright (c) 2017 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 "api/rtc_event_log_output_file.h"
12 
13 #include <fstream>
14 #include <iterator>
15 #include <memory>
16 #include <string>
17 
18 #include "rtc_base/checks.h"
19 #include "test/gtest.h"
20 #include "test/testsupport/file_utils.h"
21 
22 namespace webrtc {
23 
24 class RtcEventLogOutputFileTest : public ::testing::Test {
25  public:
RtcEventLogOutputFileTest()26   RtcEventLogOutputFileTest() : output_file_name_(GetOutputFilePath()) {
27     // Ensure no leftovers from previous runs, which might not have terminated
28     // in an orderly fashion.
29     remove(output_file_name_.c_str());
30   }
31 
~RtcEventLogOutputFileTest()32   ~RtcEventLogOutputFileTest() override { remove(output_file_name_.c_str()); }
33 
34  protected:
GetOutputFilePath() const35   std::string GetOutputFilePath() const {
36     auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
37     return test::OutputPath() + test_info->test_case_name() + test_info->name();
38   }
39 
GetOutputFileContents() const40   std::string GetOutputFileContents() const {
41     std::ifstream file(output_file_name_,
42                        std::ios_base::in | std::ios_base::binary);
43     RTC_CHECK(file.is_open());
44     RTC_CHECK(file.good());
45     std::string file_str((std::istreambuf_iterator<char>(file)),
46                          std::istreambuf_iterator<char>());
47     return file_str;
48   }
49 
50   const std::string output_file_name_;
51 };
52 
TEST_F(RtcEventLogOutputFileTest,NonDefectiveOutputsStartOutActive)53 TEST_F(RtcEventLogOutputFileTest, NonDefectiveOutputsStartOutActive) {
54   auto output_file = std::make_unique<RtcEventLogOutputFile>(output_file_name_);
55   EXPECT_TRUE(output_file->IsActive());
56 }
57 
TEST_F(RtcEventLogOutputFileTest,DefectiveOutputsStartOutInactive)58 TEST_F(RtcEventLogOutputFileTest, DefectiveOutputsStartOutInactive) {
59   const std::string illegal_filename = "/////////";
60   auto output_file = std::make_unique<RtcEventLogOutputFile>(illegal_filename);
61   EXPECT_FALSE(output_file->IsActive());
62 }
63 
64 // Sanity over opening a file (by filename) with an unlimited size.
TEST_F(RtcEventLogOutputFileTest,UnlimitedOutputFile)65 TEST_F(RtcEventLogOutputFileTest, UnlimitedOutputFile) {
66   const std::string output_str = "one two three";
67 
68   auto output_file = std::make_unique<RtcEventLogOutputFile>(output_file_name_);
69   output_file->Write(output_str);
70   output_file.reset();  // Closing the file flushes the buffer to disk.
71 
72   EXPECT_EQ(GetOutputFileContents(), output_str);
73 }
74 
75 // Do not allow writing more bytes to the file than
TEST_F(RtcEventLogOutputFileTest,LimitedOutputFileCappedToCapacity)76 TEST_F(RtcEventLogOutputFileTest, LimitedOutputFileCappedToCapacity) {
77   // Fit two bytes, then the third should be rejected.
78   auto output_file =
79       std::make_unique<RtcEventLogOutputFile>(output_file_name_, 2);
80 
81   output_file->Write("1");
82   output_file->Write("2");
83   output_file->Write("3");
84   // Unsuccessful writes close the file; no need to delete the output to flush.
85 
86   EXPECT_EQ(GetOutputFileContents(), "12");
87 }
88 
89 // Make sure that calls to Write() either write everything to the file, or
90 // nothing (short of underlying issues in the module that handles the file,
91 // which would be beyond our control).
TEST_F(RtcEventLogOutputFileTest,DoNotWritePartialLines)92 TEST_F(RtcEventLogOutputFileTest, DoNotWritePartialLines) {
93   const std::string output_str_1 = "0123456789";
94   const std::string output_str_2 = "abcdefghij";
95 
96   // Set a file size limit just shy of fitting the entire second line.
97   const size_t size_limit = output_str_1.length() + output_str_2.length() - 1;
98   auto output_file =
99       std::make_unique<RtcEventLogOutputFile>(output_file_name_, size_limit);
100 
101   output_file->Write(output_str_1);
102   output_file->Write(output_str_2);
103   // Unsuccessful writes close the file; no need to delete the output to flush.
104 
105   EXPECT_EQ(GetOutputFileContents(), output_str_1);
106 }
107 
TEST_F(RtcEventLogOutputFileTest,UnsuccessfulWriteReturnsFalse)108 TEST_F(RtcEventLogOutputFileTest, UnsuccessfulWriteReturnsFalse) {
109   auto output_file =
110       std::make_unique<RtcEventLogOutputFile>(output_file_name_, 2);
111   EXPECT_FALSE(output_file->Write("abc"));
112 }
113 
TEST_F(RtcEventLogOutputFileTest,SuccessfulWriteReturnsTrue)114 TEST_F(RtcEventLogOutputFileTest, SuccessfulWriteReturnsTrue) {
115   auto output_file =
116       std::make_unique<RtcEventLogOutputFile>(output_file_name_, 3);
117   EXPECT_TRUE(output_file->Write("abc"));
118 }
119 
120 // Even if capacity is reached, a successful write leaves the output active.
TEST_F(RtcEventLogOutputFileTest,FileStillActiveAfterSuccessfulWrite)121 TEST_F(RtcEventLogOutputFileTest, FileStillActiveAfterSuccessfulWrite) {
122   auto output_file =
123       std::make_unique<RtcEventLogOutputFile>(output_file_name_, 3);
124   ASSERT_TRUE(output_file->Write("abc"));
125   EXPECT_TRUE(output_file->IsActive());
126 }
127 
128 // Unsuccessful writes switch the output to inactive, even if capacity has
129 // not yet been reached.
TEST_F(RtcEventLogOutputFileTest,FileInactiveAfterUnsuccessfulWrite)130 TEST_F(RtcEventLogOutputFileTest, FileInactiveAfterUnsuccessfulWrite) {
131   auto output_file =
132       std::make_unique<RtcEventLogOutputFile>(output_file_name_, 2);
133   ASSERT_FALSE(output_file->Write("abc"));
134   EXPECT_FALSE(output_file->IsActive());
135 }
136 
TEST_F(RtcEventLogOutputFileTest,AllowReasonableFileSizeLimits)137 TEST_F(RtcEventLogOutputFileTest, AllowReasonableFileSizeLimits) {
138   auto output_file = std::make_unique<RtcEventLogOutputFile>(
139       output_file_name_, RtcEventLogOutputFile::kMaxReasonableFileSize);
140   EXPECT_TRUE(output_file->IsActive());
141 }
142 
143 #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
144 class RtcEventLogOutputFileDeathTest : public RtcEventLogOutputFileTest {};
145 
TEST_F(RtcEventLogOutputFileDeathTest,WritingToInactiveFileForbidden)146 TEST_F(RtcEventLogOutputFileDeathTest, WritingToInactiveFileForbidden) {
147   RtcEventLogOutputFile output_file(output_file_name_, 2);
148   ASSERT_FALSE(output_file.Write("abc"));
149   ASSERT_FALSE(output_file.IsActive());
150   EXPECT_DEATH(output_file.Write("abc"), "");
151 }
152 
TEST_F(RtcEventLogOutputFileDeathTest,DisallowUnreasonableFileSizeLimits)153 TEST_F(RtcEventLogOutputFileDeathTest, DisallowUnreasonableFileSizeLimits) {
154   // Keeping in a temporary unique_ptr to make it clearer that the death is
155   // triggered by construction, not destruction.
156   std::unique_ptr<RtcEventLogOutputFile> output_file;
157   auto create_output_file = [&] {
158     const size_t unreasonable_size =
159         RtcEventLogOutputFile::kMaxReasonableFileSize + 1;
160     output_file = std::make_unique<RtcEventLogOutputFile>(output_file_name_,
161                                                           unreasonable_size);
162   };
163   EXPECT_DEATH(create_output_file(), "");
164 }
165 #endif
166 
167 }  // namespace webrtc
168