From 4ed4f24074dd5ed0605da4e8a5721054ef903fa6 Mon Sep 17 00:00:00 2001 From: "kjellander@webrtc.org" Date: Mon, 5 Dec 2011 16:31:12 +0000 Subject: [PATCH] New fileutils.h method for managing resources on different platforms Review URL: http://webrtc-codereview.appspot.com/304007 git-svn-id: http://webrtc.googlecode.com/svn/trunk@1105 4adac7df-926f-26a2-2b94-8c16560cd09d --- test/testsupport/fileutils.cc | 105 +++++++++++++++---- test/testsupport/fileutils.h | 32 ++++++ test/testsupport/fileutils_unittest.cc | 139 +++++++++++++++++++------ 3 files changed, 220 insertions(+), 56 deletions(-) diff --git a/test/testsupport/fileutils.cc b/test/testsupport/fileutils.cc index 09d7baa349..84e9593203 100644 --- a/test/testsupport/fileutils.cc +++ b/test/testsupport/fileutils.cc @@ -25,6 +25,8 @@ #include +#include "typedefs.h" // For architecture defines + namespace webrtc { namespace test { @@ -36,18 +38,17 @@ static const char* kPathDelimiter = "/"; // The file we're looking for to identify the project root dir. static const char* kProjectRootFileName = "DEPS"; static const char* kOutputDirName = "out"; -static const char* kOutputFallbackPath = "./"; +static const char* kFallbackPath = "./"; +static const char* kResourcesDirName = "resources"; const char* kCannotFindProjectRootDir = "ERROR_CANNOT_FIND_PROJECT_ROOT_DIR"; std::string ProjectRootPath() { - char path_buffer[FILENAME_MAX]; - if (!GET_CURRENT_DIR(path_buffer, sizeof(path_buffer))) { - fprintf(stderr, "Cannot get current directory!\n"); + std::string working_dir = WorkingDir(); + if (working_dir == kFallbackPath) { return kCannotFindProjectRootDir; } - // Check for our file that verifies the root dir. - std::string current_path(path_buffer); + std::string current_path(working_dir); FILE* file = NULL; int path_delimiter_index = current_path.find_last_of(kPathDelimiter); while (path_delimiter_index > -1) { @@ -58,12 +59,10 @@ std::string ProjectRootPath() { fclose(file); return current_path + kPathDelimiter; } - // Move up one directory in the directory tree. current_path = current_path.substr(0, path_delimiter_index); path_delimiter_index = current_path.find_last_of(kPathDelimiter); } - // Reached the root directory. fprintf(stderr, "Cannot find project root directory!\n"); return kCannotFindProjectRootDir; @@ -72,25 +71,85 @@ std::string ProjectRootPath() { std::string OutputPath() { std::string path = ProjectRootPath(); if (path == kCannotFindProjectRootDir) { - return kOutputFallbackPath; + return kFallbackPath; } path += kOutputDirName; - struct stat path_info = {0}; - // Check if the path exists already: - if (stat(path.c_str(), &path_info) == 0) { - if (!S_ISDIR(path_info.st_mode)) { - fprintf(stderr, "Path %s exists but is not a directory! Remove this file " - "and re-run to create the output folder.\n", path.c_str()); - return kOutputFallbackPath; - } - } else { -#ifdef WIN32 - _mkdir(path.c_str()); -#else - mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); -#endif + if (!CreateDirectory(path)) { + return kFallbackPath; } return path + kPathDelimiter; } + +std::string WorkingDir() { + char path_buffer[FILENAME_MAX]; + if (!GET_CURRENT_DIR(path_buffer, sizeof(path_buffer))) { + fprintf(stderr, "Cannot get current directory!\n"); + return kFallbackPath; + } + else { + return std::string(path_buffer); + } +} + +bool CreateDirectory(std::string directory_name) { + struct stat path_info = {0}; + // Check if the path exists already: + if (stat(directory_name.c_str(), &path_info) == 0) { + if (!S_ISDIR(path_info.st_mode)) { + fprintf(stderr, "Path %s exists but is not a directory! Remove this " + "file and re-run to create the directory.\n", + directory_name.c_str()); + return false; + } + } else { +#ifdef WIN32 + return _mkdir(directory_name.c_str()) == 0; +#else + return mkdir(directory_name.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0; +#endif + } + return true; +} + +bool FileExists(std::string file_name) { + struct stat file_info = {0}; + return stat(file_name.c_str(), &file_info) == 0; +} + +std::string ResourcePath(std::string name, std::string extension) { + std::string platform = "win"; +#ifdef WEBRTC_LINUX + platform = "linux"; +#endif // WEBRTC_LINUX +#ifdef WEBRTC_MAC + platform = "mac"; +#endif // WEBRTC_MAC + +#ifdef WEBRTC_ARCH_64_BITS + std::string architecture = "64"; +#else + std::string architecture = "32"; +#endif // WEBRTC_ARCH_64_BITS + + std::string resources_path = ProjectRootPath() + kResourcesDirName + kPathDelimiter; + std::string resource_file = resources_path + name + "_" + platform + "_" + + architecture + "." + extension; + if (!FileExists(resource_file)) { + return resource_file; + } + // Try without architecture. + resource_file = resources_path + name + "_" + platform + "." + extension; + if (FileExists(resource_file)) { + return resource_file; + } + // Try without platform. + resource_file = resources_path + name + "_" + architecture + "." + extension; + if (FileExists(resource_file)) { + return resource_file; + } + // Fall back on name without architecture or platform. + return resources_path + name + "." + extension; +} + } // namespace test } // namespace webrtc diff --git a/test/testsupport/fileutils.h b/test/testsupport/fileutils.h index a111a7abc6..cd43c692c5 100644 --- a/test/testsupport/fileutils.h +++ b/test/testsupport/fileutils.h @@ -9,6 +9,7 @@ */ // File utilities for testing purposes. +// // The ProjectRootPath() method is a convenient way of getting an absolute // path to the project source tree root directory. Using this, it is easy to // refer to test resource files in a portable way. @@ -99,6 +100,37 @@ std::string ProjectRootPath(); // found, the current working directory ("./") is returned as a fallback. std::string OutputPath(); +// Returns a path to a resource file for the currently executing platform. +// Adapts to what filenames are currently present in the +// [project-root]/resources/ dir. +// Returns an absolute path according to this priority list (the directory +// part of the path is left out for readability): +// 1. [name]_[platform]_[architecture].[extension] +// 2. [name]_[platform].[extension] +// 3. [name]_[architecture].[extension] +// 4. [name].[extension] +// Where +// * platform is either of "win", "mac" or "linux". +// * architecture is either of "32" or "64". +// +// Arguments: +// name - Name of the resource file. If a plain filename (no directory path) +// is supplied, the file is assumed to be located in resources/ +// If a directory path is prepended to the filename, a subdirectory +// hierarchy reflecting that path is assumed to be present. +// extension - File extension, without the dot, i.e. "bmp" or "yuv". +std::string ResourcePath(std::string name, std::string extension); + +// Gets the current working directory for the executing program. +// Returns "./" if for some reason it is not possible to find the working +// directory. +std::string WorkingDir(); + +// Creates a directory if it not already exists. +// Returns true if successful. Will print an error message to stderr and return +// false if a file with the same name already exists. +bool CreateDirectory(std::string directory_name); + } // namespace test } // namespace webrtc diff --git a/test/testsupport/fileutils_unittest.cc b/test/testsupport/fileutils_unittest.cc index 493256bda8..e9aa03c509 100644 --- a/test/testsupport/fileutils_unittest.cc +++ b/test/testsupport/fileutils_unittest.cc @@ -9,52 +9,99 @@ */ #include -#include "fileutils.h" +#include +#include + #include "gtest/gtest.h" +#include "testsupport/fileutils.h" #ifdef WIN32 -#include -#define GET_CURRENT_DIR _getcwd static const char* kPathDelimiter = "\\"; #else -#include -#define GET_CURRENT_DIR getcwd static const char* kPathDelimiter = "/"; #endif +static const std::string kDummyDir = "file_utils_unittest_dummy_dir"; +static const std::string kResourcesDir = "resources"; +static const std::string kTestName = "fileutils_unittest"; +static const std::string kExtension = "tmp"; + +typedef std::list FileList; + namespace webrtc { -namespace test { // 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 { +class FileUtilsTest : public testing::Test { protected: FileUtilsTest() { - original_working_dir_ = GetWorkingDir(); } virtual ~FileUtilsTest() {} + // Runs before the first test + static void SetUpTestCase() { + original_working_dir_ = webrtc::test::WorkingDir(); + std::string resources_path = original_working_dir_ + kPathDelimiter + + kResourcesDir + kPathDelimiter; + webrtc::test::CreateDirectory(resources_path); + + files_.push_back(resources_path + kTestName + "." + kExtension); + files_.push_back(resources_path + kTestName + "_32." + kExtension); + files_.push_back(resources_path + kTestName + "_64." + kExtension); + files_.push_back(resources_path + kTestName + "_linux." + kExtension); + files_.push_back(resources_path + kTestName + "_mac." + kExtension); + files_.push_back(resources_path + kTestName + "_win." + kExtension); + files_.push_back(resources_path + kTestName + "_linux_32." + kExtension); + files_.push_back(resources_path + kTestName + "_mac_32." + kExtension); + files_.push_back(resources_path + kTestName + "_win_32." + kExtension); + files_.push_back(resources_path + kTestName + "_linux_64." + kExtension); + files_.push_back(resources_path + kTestName + "_mac_64." + kExtension); + files_.push_back(resources_path + kTestName + "_win_64." + kExtension); + + // Now that the resources dir exists, write some empty test files into it. + for (FileList::iterator file_it = files_.begin(); + file_it != files_.end(); ++file_it) { + FILE* file = fopen(file_it->c_str(), "wb"); + ASSERT_TRUE(file != NULL) << "Failed to write file: " << file_it->c_str(); + ASSERT_TRUE(fprintf(file, "%s", "Dummy data") > 0); + fclose(file); + } + // Create a dummy subdir that can be chdir'ed into for testing purposes. + empty_dummy_dir_ = original_working_dir_ + kPathDelimiter + kDummyDir; + webrtc::test::CreateDirectory(empty_dummy_dir_); + } + static void TearDownTestCase() { + // Clean up all resource files written + for (FileList::iterator file_it = files_.begin(); + file_it != files_.end(); ++file_it) { + remove(file_it->c_str()); + } + std::remove(empty_dummy_dir_.c_str()); + } void SetUp() { - chdir(original_working_dir_.c_str()); + ASSERT_EQ(chdir(original_working_dir_.c_str()), 0); } - void TearDown() {} + void TearDown() { + ASSERT_EQ(chdir(original_working_dir_.c_str()), 0); + } + protected: + static std::string empty_dummy_dir_; private: - std::string original_working_dir_; - static std::string GetWorkingDir() { - char path_buffer[FILENAME_MAX]; - EXPECT_TRUE(GET_CURRENT_DIR(path_buffer, sizeof(path_buffer))) - << "Cannot get current working directory!"; - return std::string(path_buffer); - } + static FileList files_; + static std::string original_working_dir_; }; +FileList FileUtilsTest::files_; +std::string FileUtilsTest::original_working_dir_ = ""; +std::string FileUtilsTest::empty_dummy_dir_ = ""; + // Tests that the project root path is returned for the default working // directory that is automatically set when the test executable is launched. // The test is not fully testing the implementation, since we cannot be sure // of where the executable was launched from. // The test will fail if the top level directory is not named "trunk". TEST_F(FileUtilsTest, ProjectRootPathFromUnchangedWorkingDir) { - std::string path = ProjectRootPath(); + std::string path = webrtc::test::ProjectRootPath(); std::string expected_end = "trunk"; expected_end = kPathDelimiter + expected_end + kPathDelimiter; ASSERT_EQ(path.length() - expected_end.length(), path.find(expected_end)); @@ -62,7 +109,7 @@ TEST_F(FileUtilsTest, ProjectRootPathFromUnchangedWorkingDir) { // Similar to the above test, but for the output dir TEST_F(FileUtilsTest, OutputPathFromUnchangedWorkingDir) { - std::string path = OutputPath(); + std::string path = webrtc::test::OutputPath(); std::string expected_end = "out"; expected_end = kPathDelimiter + expected_end + kPathDelimiter; ASSERT_EQ(path.length() - expected_end.length(), path.find(expected_end)); @@ -72,21 +119,19 @@ TEST_F(FileUtilsTest, OutputPathFromUnchangedWorkingDir) { // deeper from the current one. Then testing that the project path returned // is still the same, when the function under test is called again. TEST_F(FileUtilsTest, ProjectRootPathFromDeeperWorkingDir) { - std::string path = ProjectRootPath(); + std::string path = webrtc::test::ProjectRootPath(); std::string original_working_dir = path; // This is the correct project root - // Change to a subdirectory path (the full path doesn't have to exist). - path += "foo/bar/baz"; - chdir(path.c_str()); - ASSERT_EQ(original_working_dir, ProjectRootPath()); + // Change to a subdirectory path. + ASSERT_EQ(0, chdir(empty_dummy_dir_.c_str())); + ASSERT_EQ(original_working_dir, webrtc::test::ProjectRootPath()); } // Similar to the above test, but for the output dir TEST_F(FileUtilsTest, OutputPathFromDeeperWorkingDir) { - std::string path = OutputPath(); + std::string path = webrtc::test::OutputPath(); std::string original_working_dir = path; - path += "foo/bar/baz"; - chdir(path.c_str()); - ASSERT_EQ(original_working_dir, OutputPath()); + ASSERT_EQ(0, chdir(empty_dummy_dir_.c_str())); + ASSERT_EQ(original_working_dir, webrtc::test::OutputPath()); } // Tests with current working directory set to a directory higher up in the @@ -95,15 +140,43 @@ TEST_F(FileUtilsTest, OutputPathFromDeeperWorkingDir) { TEST_F(FileUtilsTest, ProjectRootPathFromRootWorkingDir) { // Change current working dir to the root of the current file system // (this will always be "above" our project root dir). - chdir(kPathDelimiter); - ASSERT_EQ(kCannotFindProjectRootDir, ProjectRootPath()); + ASSERT_EQ(0, chdir(kPathDelimiter)); + ASSERT_EQ(webrtc::test::kCannotFindProjectRootDir, + webrtc::test::ProjectRootPath()); } // Similar to the above test, but for the output dir TEST_F(FileUtilsTest, OutputPathFromRootWorkingDir) { - chdir(kPathDelimiter); - ASSERT_EQ("./", OutputPath()); + ASSERT_EQ(0, chdir(kPathDelimiter)); + ASSERT_EQ("./", webrtc::test::OutputPath()); +} + +// Only tests that the code executes +TEST_F(FileUtilsTest, CreateDirectory) { + std::string directory = "fileutils-unittest-empty-dir"; + // Make sure it's removed if a previous test has failed: + std::remove(directory.c_str()); + ASSERT_TRUE(webrtc::test::CreateDirectory(directory)); + std::remove(directory.c_str()); +} + +TEST_F(FileUtilsTest, WorkingDirReturnsValue) { + // Hard to cover all platforms. Just test that it returns something without + // crashing: + std::string working_dir = webrtc::test::WorkingDir(); + ASSERT_TRUE(working_dir.length() > 0); +} + +// Due to multiple platforms, it is hard to make a complete test for +// ResourcePath. Manual testing has been performed by removing files and +// verified the result confirms with the specified documentation for the +// function. +TEST_F(FileUtilsTest, ResourcePathReturnsValue) { + std::string resource = webrtc::test::ResourcePath(kTestName, kExtension); + ASSERT_TRUE(resource.find(kTestName) > 0); + ASSERT_TRUE(resource.find(kExtension) > 0); + ASSERT_EQ(0, chdir(kPathDelimiter)); + ASSERT_EQ("./", webrtc::test::OutputPath()); } -} // namespace test } // namespace webrtc