diff --git a/Firestore/core/src/firebase/firestore/util/filesystem.h b/Firestore/core/src/firebase/firestore/util/filesystem.h index bef66137beb..fcbf51fc886 100644 --- a/Firestore/core/src/firebase/firestore/util/filesystem.h +++ b/Firestore/core/src/firebase/firestore/util/filesystem.h @@ -21,6 +21,7 @@ #include "Firestore/core/src/firebase/firestore/util/path.h" #include "Firestore/core/src/firebase/firestore/util/status.h" +#include "Firestore/core/src/firebase/firestore/util/statusor.h" namespace firebase { namespace firestore { @@ -72,6 +73,12 @@ Status RecursivelyDelete(const Path& path); */ Path TempDir(); +/** + * On success, returns the size in bytes of the file specified by + * `path`. + */ +StatusOr FileSize(const Path& path); + /** * Implements an iterator over the contents of a directory. Initializes to the * first entry in the directory. diff --git a/Firestore/core/src/firebase/firestore/util/filesystem_posix.cc b/Firestore/core/src/firebase/firestore/util/filesystem_posix.cc index 6d3048f45bf..a8040cbd873 100644 --- a/Firestore/core/src/firebase/firestore/util/filesystem_posix.cc +++ b/Firestore/core/src/firebase/firestore/util/filesystem_posix.cc @@ -95,6 +95,16 @@ Path TempDir() { } #endif // !defined(__APPLE__) +StatusOr FileSize(const Path& path) { + struct stat st {}; + if (stat(path.c_str(), &st) == 0) { + return StatusOr(st.st_size); + } else { + return StatusOr(Status::FromErrno( + errno, StringFormat("Failed to stat file: %s", path.ToUtf8String()))); + } +} + namespace detail { Status CreateDir(const Path& path) { diff --git a/Firestore/core/test/firebase/firestore/util/filesystem_test.cc b/Firestore/core/test/firebase/firestore/util/filesystem_test.cc index 7dba108db0e..98b3924d1dd 100644 --- a/Firestore/core/test/firebase/firestore/util/filesystem_test.cc +++ b/Firestore/core/test/firebase/firestore/util/filesystem_test.cc @@ -45,6 +45,15 @@ static Path TestFilename() { return Path::FromUtf8("firestore-testing-" + CreateAutoId()); } +static void WriteBytesToFile(const Path& path, int byte_count) { + std::string bytes(byte_count, 'a'); + std::ofstream out{path.native_value()}; + ASSERT_TRUE(out.good()); + out << bytes; + out.close(); + ASSERT_TRUE(out.good()); +} + #define ASSERT_NOT_FOUND(expression) \ do { \ ASSERT_EQ(FirestoreErrorCode::NotFound, (expression).code()); \ @@ -236,6 +245,22 @@ TEST(FilesystemTest, RecursivelyDeletePreservesPeers) { EXPECT_OK(RecursivelyDelete(root_dir)); } +TEST(FilesystemTest, FileSize) { + Path file = Path::JoinUtf8(TempDir(), TestFilename()); + ASSERT_NOT_FOUND(FileSize(file).status()); + Touch(file); + StatusOr result = FileSize(file); + ASSERT_OK(result.status()); + ASSERT_EQ(0, result.ValueOrDie()); + + WriteBytesToFile(file, 100); + result = FileSize(file); + ASSERT_OK(result.status()); + ASSERT_EQ(100, result.ValueOrDie()); + + EXPECT_OK(RecursivelyDelete(file)); +} + } // namespace util } // namespace firestore } // namespace firebase