Promote RtcEventLogOutputFile to api/

Preparation for deleting PeerConnectionInterface::StartRtcEventLog
method with a PlatformFile argument.

Bug: webrtc:6463
Change-Id: Ia9fa1d99a3d87f3bf193e73382690b782ffea65c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/135285
Commit-Queue: Niels Moller <nisse@webrtc.org>
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27879}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index ec27409..2512bd4 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -348,6 +348,22 @@
   ]
 }
 
+rtc_source_set("rtc_event_log_output_file") {
+  visibility = [ "*" ]
+  sources = [
+    "rtc_event_log_output_file.cc",
+    "rtc_event_log_output_file.h",
+  ]
+
+  deps = [
+    ":libjingle_logging_api",
+    "../logging:rtc_event_log_api",
+    "../rtc_base:checks",
+    "../rtc_base:rtc_base_approved",
+    "../rtc_base/system:file_wrapper",
+  ]
+}
+
 rtc_source_set("ortc_api") {
   visibility = [ "*" ]
   sources = [
@@ -804,6 +820,7 @@
       "array_view_unittest.cc",
       "function_view_unittest.cc",
       "rtc_error_unittest.cc",
+      "rtc_event_log_output_file_unittest.cc",
       "rtp_parameters_unittest.cc",
       "test/loopback_media_transport_unittest.cc",
     ]
@@ -813,13 +830,16 @@
       ":function_view",
       ":libjingle_peerconnection_api",
       ":loopback_media_transport",
+      ":rtc_event_log_output_file",
       "../rtc_base:checks",
       "../rtc_base:gunit_helpers",
       "../rtc_base:rtc_base_approved",
+      "../test:fileutils",
       "../test:test_support",
       "task_queue:task_queue_default_factory_unittests",
       "units:units_unittests",
       "video:video_unittests",
+      "//third_party/abseil-cpp/absl/memory",
     ]
   }
 
diff --git a/api/DEPS b/api/DEPS
index 34bdfe0..9c174a1 100644
--- a/api/DEPS
+++ b/api/DEPS
@@ -148,7 +148,12 @@
   "rtc_error\.h": [
     "+rtc_base/logging.h",
   ],
-
+  "rtc_event_log_output_file.h": [
+    # TODO(bugs.webrtc.org/6463): Delete this dependency.
+    "+rtc_base/platform_file.h",
+    # For private member and constructor.
+    "+rtc_base/system/file_wrapper.h",
+  ],
   "rtp_receiver_interface\.h": [
     "+rtc_base/ref_count.h",
   ],
diff --git a/api/rtc_event_log_output_file.cc b/api/rtc_event_log_output_file.cc
new file mode 100644
index 0000000..3845071
--- /dev/null
+++ b/api/rtc_event_log_output_file.cc
@@ -0,0 +1,100 @@
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <limits>
+#include <utility>
+
+#include "api/rtc_event_log_output_file.h"
+#include "logging/rtc_event_log/rtc_event_log.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+namespace {
+
+FileWrapper FileWrapperFromPlatformFile(rtc::PlatformFile platform_file) {
+  if (platform_file == rtc::kInvalidPlatformFileValue)
+    return FileWrapper();
+
+  return FileWrapper(rtc::FdopenPlatformFileForWriting(platform_file));
+}
+
+}  // namespace
+
+// Together with the assumption of no single Write() would ever be called on
+// an input with length greater-than-or-equal-to (max(size_t) / 2), this
+// guarantees no overflow of the check for remaining file capacity in Write().
+// This does *not* apply to files with unlimited size.
+const size_t RtcEventLogOutputFile::kMaxReasonableFileSize =
+    std::numeric_limits<size_t>::max() / 2;
+
+RtcEventLogOutputFile::RtcEventLogOutputFile(const std::string& file_name)
+    : RtcEventLogOutputFile(FileWrapper::OpenWriteOnly(file_name),
+                            RtcEventLog::kUnlimitedOutput) {}
+
+RtcEventLogOutputFile::RtcEventLogOutputFile(const std::string& file_name,
+                                             size_t max_size_bytes)
+
+    // Unlike plain fopen, FileWrapper takes care of filename utf8 ->
+    // wchar conversion on Windows.
+    : RtcEventLogOutputFile(FileWrapper::OpenWriteOnly(file_name),
+                            max_size_bytes) {}
+
+RtcEventLogOutputFile::RtcEventLogOutputFile(FILE* file, size_t max_size_bytes)
+    : RtcEventLogOutputFile(FileWrapper(file), max_size_bytes) {}
+
+RtcEventLogOutputFile::RtcEventLogOutputFile(FileWrapper file,
+                                             size_t max_size_bytes)
+    : max_size_bytes_(max_size_bytes), file_(std::move(file)) {
+  RTC_CHECK_LE(max_size_bytes_, kMaxReasonableFileSize);
+  if (!file_.is_open()) {
+    RTC_LOG(LS_ERROR) << "Invalid file. WebRTC event log not started.";
+  }
+}
+
+RtcEventLogOutputFile::RtcEventLogOutputFile(rtc::PlatformFile platform_file,
+                                             size_t max_size_bytes)
+    : RtcEventLogOutputFile(FileWrapperFromPlatformFile(platform_file),
+                            max_size_bytes) {}
+
+bool RtcEventLogOutputFile::IsActive() const {
+  return IsActiveInternal();
+}
+
+bool RtcEventLogOutputFile::Write(const std::string& output) {
+  RTC_DCHECK(IsActiveInternal());
+  // No single write may be so big, that it would risk overflowing the
+  // calculation of (written_bytes_ + output.length()).
+  RTC_DCHECK_LT(output.length(), kMaxReasonableFileSize);
+
+  if (max_size_bytes_ == RtcEventLog::kUnlimitedOutput ||
+      written_bytes_ + output.length() <= max_size_bytes_) {
+    if (file_.Write(output.c_str(), output.size())) {
+      written_bytes_ += output.size();
+      return true;
+    } else {
+      RTC_LOG(LS_ERROR) << "Write to WebRtcEventLog file failed.";
+    }
+  } else {
+    RTC_LOG(LS_VERBOSE) << "Max file size reached.";
+  }
+
+  // Failed, for one of above reasons. Close output file.
+  file_.Close();
+  return false;
+}
+
+// Internal non-virtual method.
+bool RtcEventLogOutputFile::IsActiveInternal() const {
+  return file_.is_open();
+}
+
+}  // namespace webrtc
diff --git a/api/rtc_event_log_output_file.h b/api/rtc_event_log_output_file.h
new file mode 100644
index 0000000..e536dfc
--- /dev/null
+++ b/api/rtc_event_log_output_file.h
@@ -0,0 +1,62 @@
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_RTC_EVENT_LOG_OUTPUT_FILE_H_
+#define API_RTC_EVENT_LOG_OUTPUT_FILE_H_
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string>
+
+#include "api/rtc_event_log_output.h"
+#include "rtc_base/platform_file.h"  // Can't neatly forward PlatformFile.
+#include "rtc_base/system/file_wrapper.h"
+
+namespace webrtc {
+
+class RtcEventLogOutputFile final : public RtcEventLogOutput {
+ public:
+  static const size_t kMaxReasonableFileSize;  // Explanation at declaration.
+
+  // Unlimited/limited-size output file (by filename).
+  explicit RtcEventLogOutputFile(const std::string& file_name);
+  RtcEventLogOutputFile(const std::string& file_name, size_t max_size_bytes);
+
+  // Limited-size output file (by FILE*). This class takes ownership
+  // of the FILE*, and closes it on destruction.
+  RtcEventLogOutputFile(FILE* file, size_t max_size_bytes);
+
+  // TODO(bugs.webrtc.org/6463): Deprecated, delete together with the
+  // corresponding PeerConnection::StartRtcEventLog override.
+  RtcEventLogOutputFile(rtc::PlatformFile file, size_t max_size_bytes);
+
+  ~RtcEventLogOutputFile() override = default;
+
+  bool IsActive() const override;
+
+  bool Write(const std::string& output) override;
+
+ private:
+  RtcEventLogOutputFile(FileWrapper file, size_t max_size_bytes);
+
+  // IsActive() can be called either from outside or from inside, but we don't
+  // want to incur the overhead of a virtual function call if called from inside
+  // some other function of this class.
+  inline bool IsActiveInternal() const;
+
+  // Maximum size, or zero for no limit.
+  const size_t max_size_bytes_;
+  size_t written_bytes_{0};
+  FileWrapper file_;
+};
+
+}  // namespace webrtc
+
+#endif  // API_RTC_EVENT_LOG_OUTPUT_FILE_H_
diff --git a/api/rtc_event_log_output_file_unittest.cc b/api/rtc_event_log_output_file_unittest.cc
new file mode 100644
index 0000000..bffda0c
--- /dev/null
+++ b/api/rtc_event_log_output_file_unittest.cc
@@ -0,0 +1,168 @@
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/rtc_event_log_output_file.h"
+
+#include <fstream>
+#include <iterator>
+#include <memory>
+#include <string>
+
+#include "absl/memory/memory.h"
+#include "rtc_base/checks.h"
+#include "test/gtest.h"
+#include "test/testsupport/file_utils.h"
+
+namespace webrtc {
+
+class RtcEventLogOutputFileTest : public ::testing::Test {
+ public:
+  RtcEventLogOutputFileTest() : output_file_name_(GetOutputFilePath()) {
+    // Ensure no leftovers from previous runs, which might not have terminated
+    // in an orderly fashion.
+    remove(output_file_name_.c_str());
+  }
+
+  ~RtcEventLogOutputFileTest() override { remove(output_file_name_.c_str()); }
+
+ protected:
+  std::string GetOutputFilePath() const {
+    auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
+    return test::OutputPath() + test_info->test_case_name() + test_info->name();
+  }
+
+  std::string GetOutputFileContents() const {
+    std::ifstream file(output_file_name_,
+                       std::ios_base::in | std::ios_base::binary);
+    RTC_CHECK(file.is_open());
+    RTC_CHECK(file.good());
+    std::string file_str((std::istreambuf_iterator<char>(file)),
+                         std::istreambuf_iterator<char>());
+    return file_str;
+  }
+
+  const std::string output_file_name_;
+};
+
+TEST_F(RtcEventLogOutputFileTest, NonDefectiveOutputsStartOutActive) {
+  auto output_file =
+      absl::make_unique<RtcEventLogOutputFile>(output_file_name_);
+  EXPECT_TRUE(output_file->IsActive());
+}
+
+TEST_F(RtcEventLogOutputFileTest, DefectiveOutputsStartOutInactive) {
+  const std::string illegal_filename = "/////////";
+  auto output_file = absl::make_unique<RtcEventLogOutputFile>(illegal_filename);
+  EXPECT_FALSE(output_file->IsActive());
+}
+
+// Sanity over opening a file (by filename) with an unlimited size.
+TEST_F(RtcEventLogOutputFileTest, UnlimitedOutputFile) {
+  const std::string output_str = "one two three";
+
+  auto output_file =
+      absl::make_unique<RtcEventLogOutputFile>(output_file_name_);
+  output_file->Write(output_str);
+  output_file.reset();  // Closing the file flushes the buffer to disk.
+
+  EXPECT_EQ(GetOutputFileContents(), output_str);
+}
+
+// Do not allow writing more bytes to the file than
+TEST_F(RtcEventLogOutputFileTest, LimitedOutputFileCappedToCapacity) {
+  // Fit two bytes, then the third should be rejected.
+  auto output_file =
+      absl::make_unique<RtcEventLogOutputFile>(output_file_name_, 2);
+
+  output_file->Write("1");
+  output_file->Write("2");
+  output_file->Write("3");
+  // Unsuccessful writes close the file; no need to delete the output to flush.
+
+  EXPECT_EQ(GetOutputFileContents(), "12");
+}
+
+// Make sure that calls to Write() either write everything to the file, or
+// nothing (short of underlying issues in the module that handles the file,
+// which would be beyond our control).
+TEST_F(RtcEventLogOutputFileTest, DoNotWritePartialLines) {
+  const std::string output_str_1 = "0123456789";
+  const std::string output_str_2 = "abcdefghij";
+
+  // Set a file size limit just shy of fitting the entire second line.
+  const size_t size_limit = output_str_1.length() + output_str_2.length() - 1;
+  auto output_file =
+      absl::make_unique<RtcEventLogOutputFile>(output_file_name_, size_limit);
+
+  output_file->Write(output_str_1);
+  output_file->Write(output_str_2);
+  // Unsuccessful writes close the file; no need to delete the output to flush.
+
+  EXPECT_EQ(GetOutputFileContents(), output_str_1);
+}
+
+TEST_F(RtcEventLogOutputFileTest, UnsuccessfulWriteReturnsFalse) {
+  auto output_file =
+      absl::make_unique<RtcEventLogOutputFile>(output_file_name_, 2);
+  EXPECT_FALSE(output_file->Write("abc"));
+}
+
+TEST_F(RtcEventLogOutputFileTest, SuccessfulWriteReturnsTrue) {
+  auto output_file =
+      absl::make_unique<RtcEventLogOutputFile>(output_file_name_, 3);
+  EXPECT_TRUE(output_file->Write("abc"));
+}
+
+// Even if capacity is reached, a successful write leaves the output active.
+TEST_F(RtcEventLogOutputFileTest, FileStillActiveAfterSuccessfulWrite) {
+  auto output_file =
+      absl::make_unique<RtcEventLogOutputFile>(output_file_name_, 3);
+  ASSERT_TRUE(output_file->Write("abc"));
+  EXPECT_TRUE(output_file->IsActive());
+}
+
+// Unsuccessful writes switch the output to inactive, even if capacity has
+// not yet been reached.
+TEST_F(RtcEventLogOutputFileTest, FileInactiveAfterUnsuccessfulWrite) {
+  auto output_file =
+      absl::make_unique<RtcEventLogOutputFile>(output_file_name_, 2);
+  ASSERT_FALSE(output_file->Write("abc"));
+  EXPECT_FALSE(output_file->IsActive());
+}
+
+TEST_F(RtcEventLogOutputFileTest, AllowReasonableFileSizeLimits) {
+  auto output_file = absl::make_unique<RtcEventLogOutputFile>(
+      output_file_name_, RtcEventLogOutputFile::kMaxReasonableFileSize);
+  EXPECT_TRUE(output_file->IsActive());
+}
+
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+TEST_F(RtcEventLogOutputFileTest, WritingToInactiveFileForbidden) {
+  RtcEventLogOutputFile output_file(output_file_name_, 2);
+  ASSERT_FALSE(output_file.Write("abc"));
+  ASSERT_FALSE(output_file.IsActive());
+  EXPECT_DEATH(output_file.Write("abc"), "");
+}
+
+TEST_F(RtcEventLogOutputFileTest, DisallowUnreasonableFileSizeLimits) {
+  // Keeping in a temporary unique_ptr to make it clearer that the death is
+  // triggered by construction, not destruction.
+  std::unique_ptr<RtcEventLogOutputFile> output_file;
+  auto create_output_file = [&] {
+    const size_t unreasonable_size =
+        RtcEventLogOutputFile::kMaxReasonableFileSize + 1;
+    output_file = absl::make_unique<RtcEventLogOutputFile>(output_file_name_,
+                                                           unreasonable_size);
+  };
+  EXPECT_DEATH(create_output_file(), "");
+}
+#endif
+
+}  // namespace webrtc