diff --git a/BUILD.gn b/BUILD.gn index c0b47d5d11..60e8fa457d 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -555,6 +555,7 @@ if (rtc_include_tests && !build_with_chromium) { "rtc_base:untyped_function_unittest", "rtc_base:weak_ptr_unittests", "rtc_base/experiments:experiments_unittests", + "rtc_base/system:file_wrapper_unittests", "rtc_base/task_utils:pending_task_safety_flag_unittests", "rtc_base/task_utils:to_queued_task_unittests", "sdk:sdk_tests", diff --git a/rtc_base/system/BUILD.gn b/rtc_base/system/BUILD.gn index 9f83c629e2..c604796e60 100644 --- a/rtc_base/system/BUILD.gn +++ b/rtc_base/system/BUILD.gn @@ -32,6 +32,19 @@ rtc_library("file_wrapper") { ] } +if (rtc_include_tests) { + rtc_library("file_wrapper_unittests") { + testonly = true + sources = [ "file_wrapper_unittest.cc" ] + deps = [ + ":file_wrapper", + "//rtc_base:checks", + "//test:fileutils", + "//test:test_support", + ] + } +} + rtc_source_set("ignore_warnings") { sources = [ "ignore_warnings.h" ] } diff --git a/rtc_base/system/file_wrapper.cc b/rtc_base/system/file_wrapper.cc index 2828790e09..3e49315793 100644 --- a/rtc_base/system/file_wrapper.cc +++ b/rtc_base/system/file_wrapper.cc @@ -89,6 +89,22 @@ bool FileWrapper::SeekTo(int64_t position) { return fseek(file_, rtc::checked_cast(position), SEEK_SET) == 0; } +long FileWrapper::FileSize() { + if (file_ == nullptr) + return -1; + long original_position = ftell(file_); + if (original_position < 0) + return -1; + int seek_error = fseek(file_, 0, SEEK_END); + if (seek_error) + return -1; + long file_size = ftell(file_); + seek_error = fseek(file_, original_position, SEEK_SET); + if (seek_error) + return -1; + return file_size; +} + bool FileWrapper::Flush() { RTC_DCHECK(file_); return fflush(file_) == 0; diff --git a/rtc_base/system/file_wrapper.h b/rtc_base/system/file_wrapper.h index 42c463cb15..0b293d9a80 100644 --- a/rtc_base/system/file_wrapper.h +++ b/rtc_base/system/file_wrapper.h @@ -38,7 +38,6 @@ class FileWrapper final { static FileWrapper OpenReadOnly(const std::string& file_name_utf8); static FileWrapper OpenWriteOnly(const char* file_name_utf8, int* error = nullptr); - static FileWrapper OpenWriteOnly(const std::string& file_name_utf8, int* error = nullptr); @@ -87,6 +86,11 @@ class FileWrapper final { // Seek to given position. bool SeekTo(int64_t position); + // Returns the file size or -1 if a size could not be determined. + // (A file size might not exists for non-seekable files or file-like + // objects, for example /dev/tty on unix.) + long FileSize(); + // Returns number of bytes read. Short count indicates EOF or error. size_t Read(void* buf, size_t length); diff --git a/rtc_base/system/file_wrapper_unittest.cc b/rtc_base/system/file_wrapper_unittest.cc new file mode 100644 index 0000000000..980b565c73 --- /dev/null +++ b/rtc_base/system/file_wrapper_unittest.cc @@ -0,0 +1,69 @@ +/* + * Copyright 2021 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 "rtc_base/system/file_wrapper.h" + +#include "rtc_base/checks.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { + +TEST(FileWrapper, FileSize) { + auto test_info = ::testing::UnitTest::GetInstance()->current_test_info(); + std::string test_name = + std::string(test_info->test_case_name()) + "_" + test_info->name(); + std::replace(test_name.begin(), test_name.end(), '/', '_'); + const std::string temp_filename = test::OutputPath() + test_name; + + // Write + { + FileWrapper file = FileWrapper::OpenWriteOnly(temp_filename); + ASSERT_TRUE(file.is_open()); + EXPECT_EQ(file.FileSize(), 0); + + EXPECT_TRUE(file.Write("foo", 3)); + EXPECT_EQ(file.FileSize(), 3); + + // FileSize() doesn't change the file size. + EXPECT_EQ(file.FileSize(), 3); + + // FileSize() doesn't move the write position. + EXPECT_TRUE(file.Write("bar", 3)); + EXPECT_EQ(file.FileSize(), 6); + } + + // Read + { + FileWrapper file = FileWrapper::OpenReadOnly(temp_filename); + ASSERT_TRUE(file.is_open()); + EXPECT_EQ(file.FileSize(), 6); + + char buf[10]; + size_t bytes_read = file.Read(buf, 3); + EXPECT_EQ(bytes_read, 3u); + EXPECT_EQ(memcmp(buf, "foo", 3), 0); + + // FileSize() doesn't move the read position. + EXPECT_EQ(file.FileSize(), 6); + + // Attempting to read past the end reads what is available + // and sets the EOF flag. + bytes_read = file.Read(buf, 5); + EXPECT_EQ(bytes_read, 3u); + EXPECT_EQ(memcmp(buf, "bar", 3), 0); + EXPECT_TRUE(file.ReadEof()); + } + + // Clean up temporary file. + remove(temp_filename.c_str()); +} + +} // namespace webrtc