|  | /* | 
|  | *  Copyright (c) 2012 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 "test/testsupport/file_utils.h" | 
|  |  | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <cstdio> | 
|  | #include <fstream> | 
|  | #include <optional> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "rtc_base/checks.h" | 
|  | #include "rtc_base/crypto_random.h" | 
|  | #include "test/gmock.h" | 
|  | #include "test/gtest.h" | 
|  |  | 
|  | #ifdef WIN32 | 
|  | #define chdir _chdir | 
|  | #endif | 
|  |  | 
|  | #if defined(WEBRTC_POSIX) | 
|  | #include <unistd.h> | 
|  | #endif | 
|  |  | 
|  | using ::testing::EndsWith; | 
|  |  | 
|  | namespace webrtc { | 
|  | namespace test { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | std::string Path(absl::string_view path) { | 
|  | std::string result(path); | 
|  | std::replace(result.begin(), result.end(), '/', kPathDelimiter[0]); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | // Remove files and directories in a directory non-recursively and writes the | 
|  | // number of deleted items in `num_deleted_entries`. | 
|  | void CleanDir(absl::string_view dir, size_t* num_deleted_entries) { | 
|  | RTC_DCHECK(num_deleted_entries); | 
|  | *num_deleted_entries = 0; | 
|  | std::optional<std::vector<std::string>> dir_content = ReadDirectory(dir); | 
|  | EXPECT_TRUE(dir_content); | 
|  | for (const auto& entry : *dir_content) { | 
|  | if (DirExists(entry)) { | 
|  | EXPECT_TRUE(RemoveDir(entry)); | 
|  | (*num_deleted_entries)++; | 
|  | } else if (FileExists(entry)) { | 
|  | EXPECT_TRUE(RemoveFile(entry)); | 
|  | (*num_deleted_entries)++; | 
|  | } else { | 
|  | FAIL(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void WriteStringInFile(absl::string_view what, absl::string_view file_path) { | 
|  | std::ofstream out(std::string{file_path}); | 
|  | out << what; | 
|  | out.close(); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Test fixture to restore the working directory between each test, since some | 
|  | // of them change it with chdir during execution (not restored by the | 
|  | // gtest framework). | 
|  | class FileUtilsTest : public ::testing::Test { | 
|  | protected: | 
|  | FileUtilsTest() {} | 
|  | ~FileUtilsTest() override {} | 
|  | // Runs before the first test | 
|  | static void SetUpTestSuite() { original_working_dir_ = test::WorkingDir(); } | 
|  | void SetUp() override { ASSERT_EQ(chdir(original_working_dir_.c_str()), 0); } | 
|  | void TearDown() override { | 
|  | ASSERT_EQ(chdir(original_working_dir_.c_str()), 0); | 
|  | } | 
|  |  | 
|  | private: | 
|  | static std::string original_working_dir_; | 
|  | }; | 
|  |  | 
|  | std::string FileUtilsTest::original_working_dir_ = ""; | 
|  |  | 
|  | // The location will vary depending on where the webrtc checkout is on the | 
|  | // system, but it should end as described above and be an absolute path. | 
|  | std::string ExpectedRootDirByPlatform() { | 
|  | #if defined(WEBRTC_ANDROID) | 
|  | return Path("chromium_tests_root/"); | 
|  | #elif defined(WEBRTC_IOS) | 
|  | return Path("tmp/"); | 
|  | #else | 
|  | return Path("out/"); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, OutputPathFromUnchangedWorkingDir) { | 
|  | std::string expected_end = ExpectedRootDirByPlatform(); | 
|  | std::string result = test::OutputPath(); | 
|  |  | 
|  | ASSERT_THAT(result, EndsWith(expected_end)); | 
|  | } | 
|  |  | 
|  | // Tests with current working directory set to a directory higher up in the | 
|  | // directory tree than the project root dir. | 
|  | TEST_F(FileUtilsTest, OutputPathFromRootWorkingDir) { | 
|  | ASSERT_EQ(0, chdir(kPathDelimiter.data())); | 
|  |  | 
|  | std::string expected_end = ExpectedRootDirByPlatform(); | 
|  | std::string result = test::OutputPath(); | 
|  |  | 
|  | ASSERT_THAT(result, EndsWith(expected_end)); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, RandomOutputPathFromUnchangedWorkingDir) { | 
|  | SetRandomTestMode(true); | 
|  | std::string fixed_first_uuid = "def01482-f829-429a-bfd4-841706e92cdd"; | 
|  | std::string expected_end = ExpectedRootDirByPlatform() + fixed_first_uuid + | 
|  | std::string(kPathDelimiter); | 
|  | std::string result = test::OutputPathWithRandomDirectory(); | 
|  |  | 
|  | ASSERT_THAT(result, EndsWith(expected_end)); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, RandomOutputPathFromRootWorkingDir) { | 
|  | ASSERT_EQ(0, chdir(kPathDelimiter.data())); | 
|  |  | 
|  | SetRandomTestMode(true); | 
|  | std::string fixed_first_uuid = "def01482-f829-429a-bfd4-841706e92cdd"; | 
|  | std::string expected_end = ExpectedRootDirByPlatform() + fixed_first_uuid + | 
|  | std::string(kPathDelimiter); | 
|  | std::string result = test::OutputPathWithRandomDirectory(); | 
|  |  | 
|  | ASSERT_THAT(result, EndsWith(expected_end)); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, TempFilename) { | 
|  | std::string temp_filename = | 
|  | test::TempFilename(test::OutputPath(), "TempFilenameTest"); | 
|  | ASSERT_TRUE(test::FileExists(temp_filename)) | 
|  | << "Couldn't find file: " << temp_filename; | 
|  | remove(temp_filename.c_str()); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, GenerateTempFilename) { | 
|  | std::string temp_filename = | 
|  | test::GenerateTempFilename(test::OutputPath(), "TempFilenameTest"); | 
|  | ASSERT_FALSE(test::FileExists(temp_filename)) | 
|  | << "File exists: " << temp_filename; | 
|  | FILE* file = fopen(temp_filename.c_str(), "wb"); | 
|  | ASSERT_TRUE(file != nullptr) << "Failed to open file: " << temp_filename; | 
|  | ASSERT_GT(fprintf(file, "%s", "Dummy data"), 0) | 
|  | << "Failed to write to file: " << temp_filename; | 
|  | fclose(file); | 
|  | remove(temp_filename.c_str()); | 
|  | } | 
|  |  | 
|  | // Only tests that the code executes | 
|  | #if defined(WEBRTC_IOS) | 
|  | #define MAYBE_CreateDir DISABLED_CreateDir | 
|  | #else | 
|  | #define MAYBE_CreateDir CreateDir | 
|  | #endif | 
|  | TEST_F(FileUtilsTest, MAYBE_CreateDir) { | 
|  | std::string directory = | 
|  | test::OutputPathWithRandomDirectory() + "fileutils-unittest-empty-dir"; | 
|  | // Make sure it's removed if a previous test has failed: | 
|  | remove(directory.c_str()); | 
|  | ASSERT_TRUE(test::CreateDir(directory)); | 
|  | remove(directory.c_str()); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, WorkingDirReturnsValue) { | 
|  | // This will obviously be different depending on where the webrtc checkout is, | 
|  | // so just check something is returned. | 
|  | std::string working_dir = test::WorkingDir(); | 
|  | ASSERT_GT(working_dir.length(), 0u); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, ResourcePathReturnsCorrectPath) { | 
|  | std::string result = | 
|  | test::ResourcePath(Path("video_coding/frame-ethernet-ii"), "pcap"); | 
|  | #if defined(WEBRTC_IOS) | 
|  | // iOS bundles resources straight into the bundle root. | 
|  | std::string expected_end = Path("/frame-ethernet-ii.pcap"); | 
|  | #else | 
|  | // Other platforms: it's a separate dir. | 
|  | std::string expected_end = | 
|  | Path("resources/video_coding/frame-ethernet-ii.pcap"); | 
|  | #endif | 
|  |  | 
|  | ASSERT_THAT(result, EndsWith(expected_end)); | 
|  | ASSERT_TRUE(FileExists(result)) << "Expected " << result | 
|  | << " to exist; did " | 
|  | "ResourcePath return an incorrect path?"; | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, ResourcePathFromRootWorkingDir) { | 
|  | ASSERT_EQ(0, chdir(kPathDelimiter.data())); | 
|  | std::string resource = test::ResourcePath("whatever", "ext"); | 
|  | #if !defined(WEBRTC_IOS) | 
|  | ASSERT_NE(resource.find("resources"), std::string::npos); | 
|  | #endif | 
|  | ASSERT_GT(resource.find("whatever"), 0u); | 
|  | ASSERT_GT(resource.find("ext"), 0u); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, GetFileSizeExistingFile) { | 
|  | // Create a file with some dummy data in. | 
|  | std::string temp_filename = | 
|  | test::TempFilename(test::OutputPath(), "fileutils_unittest"); | 
|  | FILE* file = fopen(temp_filename.c_str(), "wb"); | 
|  | ASSERT_TRUE(file != nullptr) << "Failed to open file: " << temp_filename; | 
|  | ASSERT_GT(fprintf(file, "%s", "Dummy data"), 0) | 
|  | << "Failed to write to file: " << temp_filename; | 
|  | fclose(file); | 
|  | ASSERT_GT(test::GetFileSize(temp_filename), 0u); | 
|  | remove(temp_filename.c_str()); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, GetFileSizeNonExistingFile) { | 
|  | ASSERT_EQ(0u, test::GetFileSize("non-existing-file.tmp")); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, DirExists) { | 
|  | // Check that an existing directory is recognized as such. | 
|  | ASSERT_TRUE(test::DirExists(test::OutputPath())) | 
|  | << "Existing directory not found"; | 
|  |  | 
|  | // Check that a non-existing directory is recognized as such. | 
|  | std::string directory = "direxists-unittest-non_existing-dir"; | 
|  | ASSERT_FALSE(test::DirExists(directory)) << "Non-existing directory found"; | 
|  |  | 
|  | // Check that an existing file is not recognized as an existing directory. | 
|  | std::string temp_filename = | 
|  | test::TempFilename(test::OutputPath(), "TempFilenameTest"); | 
|  | ASSERT_TRUE(test::FileExists(temp_filename)) | 
|  | << "Couldn't find file: " << temp_filename; | 
|  | ASSERT_FALSE(test::DirExists(temp_filename)) | 
|  | << "Existing file recognized as existing directory"; | 
|  | remove(temp_filename.c_str()); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, WriteReadDeleteFilesAndDirs) { | 
|  | size_t num_deleted_entries; | 
|  |  | 
|  | // Create an empty temporary directory for this test. | 
|  | const std::string temp_directory = | 
|  | OutputPathWithRandomDirectory() + Path("TempFileUtilsTestReadDirectory/"); | 
|  | CreateDir(temp_directory); | 
|  | EXPECT_NO_FATAL_FAILURE(CleanDir(temp_directory, &num_deleted_entries)); | 
|  | EXPECT_TRUE(DirExists(temp_directory)); | 
|  |  | 
|  | // Add a file. | 
|  | const std::string temp_filename = temp_directory + "TempFilenameTest"; | 
|  | WriteStringInFile("test\n", temp_filename); | 
|  | EXPECT_TRUE(FileExists(temp_filename)); | 
|  |  | 
|  | // Add an empty directory. | 
|  | const std::string temp_subdir = temp_directory + Path("subdir/"); | 
|  | EXPECT_TRUE(CreateDir(temp_subdir)); | 
|  | EXPECT_TRUE(DirExists(temp_subdir)); | 
|  |  | 
|  | // Checks. | 
|  | std::optional<std::vector<std::string>> dir_content = | 
|  | ReadDirectory(temp_directory); | 
|  | EXPECT_TRUE(dir_content); | 
|  | EXPECT_EQ(2u, dir_content->size()); | 
|  | EXPECT_NO_FATAL_FAILURE(CleanDir(temp_directory, &num_deleted_entries)); | 
|  | EXPECT_EQ(2u, num_deleted_entries); | 
|  | EXPECT_TRUE(RemoveDir(temp_directory)); | 
|  | EXPECT_FALSE(DirExists(temp_directory)); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, DeleteNonEmptyDirectory) { | 
|  | const std::string temp_directory = | 
|  | OutputPathWithRandomDirectory() + Path("TempFileUtilsTestReadDirectory/"); | 
|  | CreateDir(temp_directory); | 
|  | EXPECT_TRUE(DirExists(temp_directory)); | 
|  |  | 
|  | // Add a file. | 
|  | const std::string temp_filename = temp_directory + "TempFilenameTest"; | 
|  | WriteStringInFile("test\n", temp_filename); | 
|  | EXPECT_TRUE(FileExists(temp_filename)); | 
|  |  | 
|  | // Add a directory with one file. | 
|  | const std::string temp_subdir = temp_directory + Path("subdir/"); | 
|  | EXPECT_TRUE(CreateDir(temp_subdir)); | 
|  | EXPECT_TRUE(DirExists(temp_subdir)); | 
|  | const std::string temp_filename2 = temp_subdir + "TempFilenameTest2"; | 
|  | WriteStringInFile("test2\n", temp_filename2); | 
|  | EXPECT_TRUE(FileExists(temp_filename2)); | 
|  |  | 
|  | // Checks. | 
|  | EXPECT_TRUE(RemoveNonEmptyDir(temp_directory)); | 
|  | EXPECT_FALSE(DirExists(temp_directory)); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, DirNameStripsFilename) { | 
|  | EXPECT_EQ(Path("/some/path"), DirName(Path("/some/path/file.txt"))); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, DirNameKeepsStrippingRightmostPathComponent) { | 
|  | EXPECT_EQ(Path("/some"), DirName(DirName(Path("/some/path/file.txt")))); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, DirNameDoesntCareIfAPathEndsInPathSeparator) { | 
|  | EXPECT_EQ(Path("/some"), DirName(Path("/some/path/"))); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, DirNameStopsAtRoot) { | 
|  | EXPECT_EQ(Path("/"), DirName(Path("/"))); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, JoinFilenameDoesNotAppendExtraPathDelimiterIfExists) { | 
|  | EXPECT_EQ(JoinFilename(Path("/some/path/"), "file.txt"), | 
|  | Path("/some/path/file.txt")); | 
|  | } | 
|  |  | 
|  | TEST_F(FileUtilsTest, JoinFilenameAppendsPathDelimiterIfMissing) { | 
|  | EXPECT_EQ(JoinFilename(Path("/some/path"), "file.txt"), | 
|  | Path("/some/path/file.txt")); | 
|  | } | 
|  |  | 
|  | }  // namespace test | 
|  | }  // namespace webrtc |