diff options
-rw-r--r-- | base/base.xcodeproj/project.pbxproj | 2 | ||||
-rw-r--r-- | base/base_paths.cc | 7 | ||||
-rw-r--r-- | base/base_paths.h | 3 | ||||
-rw-r--r-- | base/base_paths_mac.h | 10 | ||||
-rw-r--r-- | base/base_paths_mac.mm | 10 | ||||
-rw-r--r-- | base/base_paths_win.cc | 7 | ||||
-rw-r--r-- | base/base_paths_win.h | 3 | ||||
-rw-r--r-- | base/file_util.cc | 40 | ||||
-rw-r--r-- | base/file_util.h | 38 | ||||
-rw-r--r-- | base/file_util_posix.cc | 219 | ||||
-rw-r--r-- | base/file_util_unittest.cc | 247 |
11 files changed, 420 insertions, 166 deletions
diff --git a/base/base.xcodeproj/project.pbxproj b/base/base.xcodeproj/project.pbxproj index d52aee6..a1c1ba1 100644 --- a/base/base.xcodeproj/project.pbxproj +++ b/base/base.xcodeproj/project.pbxproj @@ -102,6 +102,7 @@ A5A026550E4A214600498DA9 /* file_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = A5A026540E4A214600498DA9 /* file_util.cc */; }; A5A0268E0E4A2BDC00498DA9 /* file_util_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = A5A0268D0E4A2BDC00498DA9 /* file_util_posix.cc */; }; A5A0270B0E4A630D00498DA9 /* file_util_mac.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5A0270A0E4A630D00498DA9 /* file_util_mac.mm */; }; + A5CE1D2B0E55F4D800AD0606 /* file_util_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = A5A0282D0E4CFA8500498DA9 /* file_util_unittest.cc */; }; ABF4B98F0DC2BA6900A6E319 /* base_paths_mac.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABF4B98E0DC2BA6900A6E319 /* base_paths_mac.mm */; }; ABF4B99E0DC2BB6000A6E319 /* clipboard_mac.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABF4B99D0DC2BB6000A6E319 /* clipboard_mac.mm */; }; ABF4B9AF0DC2BC6200A6E319 /* json_reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254031B0D92D1F40006B936 /* json_reader.cc */; }; @@ -1111,6 +1112,7 @@ files = ( 7B78D38E0E54FE0100609465 /* at_exit_unittest.cc in Sources */, 7B78D38F0E54FE0100609465 /* command_line_unittest.cc in Sources */, + A5CE1D2B0E55F4D800AD0606 /* file_util_unittest.cc in Sources */, 7B78D3910E54FE0100609465 /* file_version_info_unittest.cc in Sources */, 7B78D3920E54FE0100609465 /* json_reader_unittest.cc in Sources */, 7B78D3930E54FE0100609465 /* json_writer_unittest.cc in Sources */, diff --git a/base/base_paths.cc b/base/base_paths.cc index ed0a489..e68a253 100644 --- a/base/base_paths.cc +++ b/base/base_paths.cc @@ -51,13 +51,6 @@ bool PathProvider(int key, std::wstring* result) { if (!file_util::GetTempDir(&cur)) return false; break; - case base::DIR_SOURCE_ROOT: - // By default, unit tests execute two levels deep from the source root. - // For example: chrome/{Debug|Release}/ui_tests.exe - PathProvider(base::DIR_EXE, &cur); - file_util::UpOneDirectory(&cur); - file_util::UpOneDirectory(&cur); - break; default: return false; } diff --git a/base/base_paths.h b/base/base_paths.h index 31344a2..2fea742 100644 --- a/base/base_paths.h +++ b/base/base_paths.h @@ -51,9 +51,6 @@ enum { DIR_EXE, // directory containing FILE_EXE DIR_MODULE, // directory containing FILE_MODULE DIR_TEMP, // temporary directory - DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful - // for tests that need to locate various resources. It - // should not be used outside of test code. PATH_END }; diff --git a/base/base_paths_mac.h b/base/base_paths_mac.h index 2097a65..e368dbc 100644 --- a/base/base_paths_mac.h +++ b/base/base_paths_mac.h @@ -42,10 +42,12 @@ enum { FILE_EXE, // path and filename of the current executable FILE_MODULE, // path and filename of the module containing the code for the // PathService (which could differ from FILE_EXE if the - // PathService were compiled into a DLL, for example) - DIR_APP_DATA, // Application Data directory under the user profile. - DIR_LOCAL_APP_DATA, // "Local Settings\Application Data" directory under the - // user profile. + // PathService were compiled into a library, for example) + DIR_APP_DATA, // ~/Library/Application Support/Google/Chrome + DIR_LOCAL_APP_DATA, // same as above (can we remove?) + DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful + // for tests that need to locate various resources. It + // should not be used outside of test code. PATH_MAC_END }; diff --git a/base/base_paths_mac.mm b/base/base_paths_mac.mm index c88a433..e5b0c65 100644 --- a/base/base_paths_mac.mm +++ b/base/base_paths_mac.mm @@ -31,7 +31,9 @@ #import <Cocoa/Cocoa.h> +#include "base/file_util.h" #include "base/logging.h" +#include "base/path_service.h" #include "base/string_util.h" namespace base { @@ -61,6 +63,14 @@ bool PathProviderMac(int key, std::wstring* result) { [path cStringUsingEncoding:NSUTF32StringEncoding]); break; } + case base::DIR_SOURCE_ROOT: + // On the mac, unit tests execute three levels deep from the source root. + // For example: chrome/build/{Debug|Release}/ui_tests + PathService::Get(base::DIR_EXE, &cur); + file_util::UpOneDirectory(&cur); + file_util::UpOneDirectory(&cur); + file_util::UpOneDirectory(&cur); + break; default: return false; } diff --git a/base/base_paths_win.cc b/base/base_paths_win.cc index 432bc90..0fe0101 100644 --- a/base/base_paths_win.cc +++ b/base/base_paths_win.cc @@ -124,6 +124,13 @@ bool PathProviderWin(int key, std::wstring* result) { return false; cur = system_buffer; break; + case base::DIR_SOURCE_ROOT: + // On Windows, unit tests execute two levels deep from the source root. + // For example: chrome/{Debug|Release}/ui_tests.exe + PathProvider(base::DIR_EXE, &cur); + file_util::UpOneDirectory(&cur); + file_util::UpOneDirectory(&cur); + break; default: return false; } diff --git a/base/base_paths_win.h b/base/base_paths_win.h index 6905ac6..8bb47d1ff 100644 --- a/base/base_paths_win.h +++ b/base/base_paths_win.h @@ -56,6 +56,9 @@ enum { DIR_LOCAL_APP_DATA_LOW, // Local AppData directory for low integrity level. DIR_LOCAL_APP_DATA, // "Local Settings\Application Data" directory under the // user profile. + DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful + // for tests that need to locate various resources. It + // should not be used outside of test code. PATH_WIN_END }; diff --git a/base/file_util.cc b/base/file_util.cc index 234c466..4594144 100644 --- a/base/file_util.cc +++ b/base/file_util.cc @@ -30,7 +30,6 @@ #include "base/file_util.h" #include <fstream> -#include <string> #include "base/logging.h" #include "base/string_util.h" @@ -40,12 +39,43 @@ namespace file_util { const wchar_t kExtensionSeparator = L'.'; +void PathComponents(const std::wstring& path, + std::vector<std::wstring>* components) { + DCHECK(components != NULL); + if (components == NULL) + return; + std::wstring::size_type start = 0; + std::wstring::size_type end = path.find(kPathSeparator, start); + + // Special case the "/" or "\" directory. On Windows with a drive letter, + // this code path won't hit, but the right thing should still happen. + // "E:\foo" will turn into "E:","foo". + if (end == start) { + components->push_back(std::wstring(path, 0, 1)); + start = end + 1; + end = path.find(kPathSeparator, start); + } + while (end != std::wstring::npos) { + std::wstring component = std::wstring(path, start, end - start); + components->push_back(component); + start = end + 1; + end = path.find(kPathSeparator, start); + } + std::wstring component = std::wstring(path, start); + components->push_back(component); +} + bool EndsWithSeparator(std::wstring* path) { - return (!path->empty() && (*path)[path->length() - 1] == kPathSeparator); + return EndsWithSeparator(*path); +} + +bool EndsWithSeparator(const std::wstring& path) { + bool is_sep = ((path)[path.length() - 1] == kPathSeparator); + return is_sep; } void TrimTrailingSeparator(std::wstring* dir) { - while (EndsWithSeparator(dir)) + while (dir->length() > 1 && EndsWithSeparator(dir)) dir->resize(dir->length() - 1); } @@ -77,9 +107,8 @@ void TrimFilename(std::wstring* path) { } } -// TODO(mpcomplete): Make this platform-independent, etc. std::wstring GetFilenameFromPath(const std::wstring& path) { - std::wstring::size_type pos = path.find_last_of(L"\\/"); + std::wstring::size_type pos = path.find_last_of(kPathSeparator); return std::wstring(path, pos == std::wstring::npos ? 0 : pos+1); } @@ -180,6 +209,7 @@ void ReplaceIllegalCharacters(std::wstring* file_name, int replace_char) { if (illegal_characters.contains(wstr[i])) { (*file_name)[i] = replace_char; } + ++i; } #else #error wchar_t* should be either UTF-16 or UTF-32 diff --git a/base/file_util.h b/base/file_util.h index 4aac207..e3895e3 100644 --- a/base/file_util.h +++ b/base/file_util.h @@ -37,10 +37,13 @@ #if defined(OS_WIN) #include <windows.h> +#elif defined(OS_POSIX) +#include <fts.h> #endif #include <stack> #include <string> +#include <vector> #include "base/basictypes.h" @@ -55,9 +58,15 @@ extern const wchar_t kPathSeparator; //----------------------------------------------------------------------------- // Functions that operate purely on a path string w/o touching the filesystem: +// Returns a vector of all of the components of the provided path. +void PathComponents(const std::wstring& path, + std::vector<std::wstring>* components); + // Returns true if the given path ends with a path separator character. +// TODO(erikkay): remove this pointer version bool EndsWithSeparator(std::wstring* path); - +bool EndsWithSeparator(const std::wstring& path); + // Modifies a string by trimming all trailing separators from the end. void TrimTrailingSeparator(std::wstring* dir); @@ -188,6 +197,7 @@ bool ContentsEqual(const std::wstring& filename1, // Useful for unit tests. bool ReadFileToString(const std::wstring& path, std::string* contents); +#if defined(OS_WINDOWS) // Resolve Windows shortcut (.LNK file) // Argument path specifies a valid LNK file. On success, return true and put // the URL into path. If path is a invalid .LNK file, return false. @@ -218,13 +228,16 @@ bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination, const wchar_t *working_dir, const wchar_t *arguments, const wchar_t *description, const wchar_t *icon, int icon_index); +#endif + // Get the temporary directory provided by the system. bool GetTempDir(std::wstring* path); -// Creates a temporary file name, but does it not create the file. It accesses -// the disk to do this, however. The full path is placed in 'temp_file', and the -// function returns true if was successful in creating the file name. +// Creates a temporary file. The full path is placed in 'temp_file', and the +// function returns true if was successful in creating the file. The file will +// be empty and all handles closed after this function returns. +// TODO(erikkay): rename this function and track down all of the callers. bool CreateTemporaryFileName(std::wstring* temp_file); // Create a new directory under TempPath. If prefix is provided, the new @@ -275,9 +288,18 @@ class FileEnumerator { // files in one directory will be returned before any files in a // subdirectory. // - // The last parameter is an optional pattern for which files to match. This - // works like a Windows file pattern. For example, "*.txt" or "Foo???.doc". + // |file_type| specifies whether the enumerator should match files, + // directories, or both. + // + // |pattern| is an optional pattern for which files to match. This + // works like shell globbing. For example, "*.txt" or "Foo???.doc". + // However, be careful in specifying patterns that aren't cross platform + // since the underlying code uses OS-specific matching routines. In general, + // Windows matching is less featureful than others, so test there first. // If unspecified, this will match all files. + // NOTE: the pattern only matches the contents of root_path, not files in + // recursive subdirectories. + // TODO(erikkay): Fix the pattern matching to work at all levels. FileEnumerator(const std::wstring& root_path, bool recursive, FileEnumerator::FILE_TYPE file_type); @@ -307,7 +329,9 @@ class FileEnumerator { #if defined(OS_WIN) WIN32_FIND_DATA find_data_; HANDLE find_handle_; -#endif // defined(OS_WIN) +#elif defined(OS_POSIX) + FTS* fts_; +#endif DISALLOW_EVIL_CONSTRUCTORS(FileEnumerator); }; diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc index 4f77e94..4f75c8a 100644 --- a/base/file_util_posix.cc +++ b/base/file_util_posix.cc @@ -29,10 +29,13 @@ #include "base/file_util.h" -#include <sys/stat.h> -#include <sys/syslimits.h> #include <fcntl.h> +#include <fnmatch.h> +#include <fts.h> #include <libgen.h> +#include <sys/errno.h> +#include <sys/stat.h> +#include <sys/syslimits.h> #include <time.h> #include <fstream> @@ -44,28 +47,79 @@ namespace file_util { std::wstring GetDirectoryFromPath(const std::wstring& path) { - char full_path[PATH_MAX]; - base::strlcpy(full_path, WideToUTF8(path).c_str(), arraysize(full_path)); - return UTF8ToWide(dirname(full_path)); + if (EndsWithSeparator(path)) { + std::wstring dir = path; + TrimTrailingSeparator(&dir); + return dir; + } else { + char full_path[PATH_MAX]; + base::strlcpy(full_path, WideToUTF8(path).c_str(), arraysize(full_path)); + return UTF8ToWide(dirname(full_path)); + } } bool AbsolutePath(std::wstring* path) { - return ResolveShortcut(path); + char full_path[PATH_MAX]; + if (realpath(WideToUTF8(*path).c_str(), full_path) == NULL) + return false; + *path = UTF8ToWide(full_path); + return true; } +// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*" +// which works both with and without the recursive flag. I'm not sure we need +// that functionality. If not, remove from file_util_win.cc, otherwise add it +// here. bool Delete(const std::wstring& path, bool recursive) { - std::string utf8_path = WideToUTF8(path); + const char* utf8_path = WideToUTF8(path).c_str(); struct stat64 file_info; - if (stat64(utf8_path.c_str(), &file_info) != 0); - return false; + int test = stat64(utf8_path, &file_info); + if (test != 0) { + // The Windows version defines this condition as success. + bool ret = (errno == ENOENT || errno == ENOTDIR); + return ret; + } if (!S_ISDIR(file_info.st_mode)) - return (unlink(utf8_path.c_str()) == 0); + return (unlink(utf8_path) == 0); if (!recursive) - return (rmdir(utf8_path.c_str()) == 0); + return (rmdir(utf8_path) == 0); - // TODO(erikkay): delete directories - DCHECK(recursive); - return false; + bool success = true; + int ftsflags = FTS_PHYSICAL | FTS_NOSTAT; + char top_dir[PATH_MAX]; + base::strlcpy(top_dir, utf8_path, sizeof(top_dir)); + char* dir_list[2] = { top_dir, NULL }; + FTS* fts = fts_open(dir_list, ftsflags, NULL); + if (fts) { + FTSENT* fts_ent = fts_read(fts); + while (success && fts_ent != NULL) { + switch (fts_ent->fts_info) { + case FTS_DNR: + case FTS_ERR: + // log error + success = false; + continue; + break; + case FTS_DP: + rmdir(fts_ent->fts_accpath); + break; + case FTS_D: + break; + case FTS_NSOK: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + unlink(fts_ent->fts_accpath); + break; + default: + DCHECK(false); + break; + } + fts_ent = fts_read(fts); + } + fts_close(fts); + } + return success; } bool Move(const std::wstring& from_path, const std::wstring& to_path) { @@ -111,30 +165,18 @@ bool GetFileCreationLocalTime(const std::string& filename, } #endif -bool ResolveShortcut(std::wstring* path) { - char full_path[PATH_MAX]; - if (!realpath(WideToUTF8(*path).c_str(), full_path)) - return false; - *path = UTF8ToWide(full_path); - return true; -} - -bool CreateShortcutLink(const char *source, const char *destination, - const char *working_dir, const char *arguments, - const char *description, const char *icon, - int icon_index) { - // TODO(erikkay): implement - return false; -} - bool CreateTemporaryFileName(std::wstring* temp_file) { std::wstring tmpdir; if (!GetTempDir(&tmpdir)) return false; - tmpdir.append(L"/com.google.chrome.XXXXXX"); + tmpdir.append(L"com.google.chrome.XXXXXX"); // this should be OK since mktemp just replaces characters in place char* buffer = const_cast<char*>(WideToUTF8(tmpdir).c_str()); *temp_file = UTF8ToWide(mktemp(buffer)); + int fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) + return false; + close(fd); return true; } @@ -154,7 +196,21 @@ bool CreateNewTempDirectory(const std::wstring& prefix, } bool CreateDirectory(const std::wstring& full_path) { - return (mkdir(WideToUTF8(full_path).c_str(), 0777) == 0); + std::vector<std::wstring> components; + PathComponents(full_path, &components); + std::wstring path; + std::vector<std::wstring>::iterator i = components.begin(); + for (; i != components.end(); ++i) { + if (path.length() == 0) + path = *i; + else + AppendToPath(&path, *i); + if (!PathExists(path)) { + if (mkdir(WideToUTF8(path).c_str(), 0777) != 0) + return false; + } + } + return true; } bool GetFileSize(const std::wstring& file_path, int64* file_size) { @@ -200,96 +256,97 @@ bool SetCurrentDirectory(const std::wstring& current_directory) { return (ret == 0); } -// TODO(erikkay): implement -#if 0 -FileEnumerator::FileEnumerator(const std::string& root_path, +FileEnumerator::FileEnumerator(const std::wstring& root_path, bool recursive, FileEnumerator::FILE_TYPE file_type) : recursive_(recursive), file_type_(file_type), is_in_find_op_(false), - find_handle_(INVALID_HANDLE_VALUE) { + fts_(NULL) { pending_paths_.push(root_path); } -FileEnumerator::FileEnumerator(const std::string& root_path, +FileEnumerator::FileEnumerator(const std::wstring& root_path, bool recursive, FileEnumerator::FILE_TYPE file_type, - const std::string& pattern) + const std::wstring& pattern) : recursive_(recursive), file_type_(file_type), + pattern_(root_path), is_in_find_op_(false), - pattern_(pattern), - find_handle_(INVALID_HANDLE_VALUE) { + fts_(NULL) { + // The Windows version of this code only matches against items in the top-most + // directory, and we're comparing fnmatch against full paths, so this is the + // easiest way to get the right pattern. + AppendToPath(&pattern_, pattern); pending_paths_.push(root_path); } - + FileEnumerator::~FileEnumerator() { - if (find_handle_ != INVALID_HANDLE_VALUE) - FindClose(find_handle_); + if (fts_) + fts_close(fts_); } +// As it stands, this method calls itself recursively when the next item of +// the fts enumeration doesn't match (type, pattern, etc.). In the case of +// large directories with many files this can be quite deep. +// TODO(erikkay) - get rid of this recursive pattern std::wstring FileEnumerator::Next() { if (!is_in_find_op_) { if (pending_paths_.empty()) return std::wstring(); // The last find FindFirstFile operation is done, prepare a new one. - // root_path_ must have the trailing directory character. root_path_ = pending_paths_.top(); - file_util::AppendToPath(&root_path_, std::wstring()); + TrimTrailingSeparator(&root_path_); pending_paths_.pop(); // Start a new find operation. - std::wstring src(root_path_); - - if (pattern_.empty()) - file_util::AppendToPath(&src, "*"); // No pattern = match everything. - else - file_util::AppendToPath(&src, pattern_); - - find_handle_ = FindFirstFile(src.c_str(), &find_data_); + int ftsflags = FTS_LOGICAL; + char top_dir[PATH_MAX]; + base::strlcpy(top_dir, WideToUTF8(root_path_).c_str(), sizeof(top_dir)); + char* dir_list[2] = { top_dir, NULL }; + fts_ = fts_open(dir_list, ftsflags, NULL); + if (!fts_) + return Next(); is_in_find_op_ = true; - - } else { - // Search for the next file/directory. - if (!FindNextFile(find_handle_, &find_data_)) { - FindClose(find_handle_); - find_handle_ = INVALID_HANDLE_VALUE; - } } - if (INVALID_HANDLE_VALUE == find_handle_) { + FTSENT* fts_ent = fts_read(fts_); + if (fts_ent == NULL) { + fts_close(fts_); + fts_ = NULL; is_in_find_op_ = false; - - // This is reached when we have finished a directory and are advancing to - // the next one in the queue. We applied the pattern (if any) to the files - // in the root search directory, but for those directories which were - // matched, we want to enumerate all files inside them. This will happen - // when the handle is empty. - pattern_.clear(); - return Next(); } - std::wstring cur_file(find_data_.cFileName); - // Skip over . and .. - if (L"." == cur_file || L".." == cur_file) + // Level 0 is the top, which is always skipped. + if (fts_ent->fts_level == 0) return Next(); - // Construct the absolute filename. - cur_file.insert(0, root_path_); + // Patterns are only matched on the items in the top-most directory. + // (see Windows implementation) + if (fts_ent->fts_level == 1 && pattern_.length() > 0) { + const char* utf8_pattern = WideToUTF8(pattern_).c_str(); + if (fnmatch(utf8_pattern, fts_ent->fts_path, 0) != 0) { + if (fts_ent->fts_info == FTS_D) + fts_set(fts_, fts_ent, FTS_SKIP); + return Next(); + } + } - if (recursive_ && find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - // If |cur_file| is a directory, and we are doing recursive searching, add - // it to pending_paths_ so we scan it after we finish scanning this - // directory. - pending_paths_.push(cur_file); + std::wstring cur_file(UTF8ToWide(fts_ent->fts_path)); + if (fts_ent->fts_info == FTS_D) { + // If not recursive, then prune children. + if (!recursive_) + fts_set(fts_, fts_ent, FTS_SKIP); return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next(); + } else if (fts_ent->fts_info == FTS_F) { + return (file_type_ & FileEnumerator::FILES) ? cur_file : Next(); } - return (file_type_ & FileEnumerator::FILES) ? cur_file : Next(); + // TODO(erikkay) - verify that the other fts_info types aren't interesting + return Next(); } -#endif } // namespace file_util diff --git a/base/file_util_unittest.cc b/base/file_util_unittest.cc index f82a894..6ff6ef0 100644 --- a/base/file_util_unittest.cc +++ b/base/file_util_unittest.cc @@ -27,13 +27,15 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#if defined(OS_WINDOWS) #include <windows.h> -#include <set> #include <shellapi.h> #include <shlobj.h> +#endif #include <fstream> #include <iostream> +#include <set> #include "base/base_paths.h" #include "base/file_util.h" @@ -53,11 +55,11 @@ class FileUtilTest : public testing::Test { // Create a fresh, empty copy of this directory. file_util::Delete(test_dir_, true); - CreateDirectory(test_dir_.c_str(), NULL); + file_util::CreateDirectory(test_dir_.c_str()); } virtual void TearDown() { // Clean up test directory - ASSERT_TRUE(file_util::Delete(test_dir_, false)); + ASSERT_TRUE(file_util::Delete(test_dir_, true)); ASSERT_FALSE(file_util::PathExists(test_dir_)); } @@ -85,6 +87,10 @@ class FindResultCollector { bool HasFile(const std::wstring& file) const { return files_.find(file) != files_.end(); } + + int size() { + return files_.size(); + } private: std::set<std::wstring> files_; @@ -94,7 +100,7 @@ class FindResultCollector { void CreateTextFile(const std::wstring& filename, const std::wstring& contents) { std::ofstream file; - file.open(filename.c_str()); + file.open(WideToUTF8(filename).c_str()); ASSERT_TRUE(file.is_open()); file << contents; file.close(); @@ -102,27 +108,30 @@ void CreateTextFile(const std::wstring& filename, // Simple function to take out some text from a file. std::wstring ReadTextFile(const std::wstring& filename) { - WCHAR contents[64]; + wchar_t contents[64]; std::wifstream file; - file.open(filename.c_str()); + file.open(WideToUTF8(filename).c_str()); EXPECT_TRUE(file.is_open()); file.getline(contents, 64); file.close(); return std::wstring(contents); } +#if defined OS_WINDOWS uint64 FileTimeAsUint64(const FILETIME& ft) { ULARGE_INTEGER u; u.LowPart = ft.dwLowDateTime; u.HighPart = ft.dwHighDateTime; return u.QuadPart; } +#endif const struct append_case { const wchar_t* path; const wchar_t* ending; const wchar_t* result; } append_cases[] = { +#if defined(OS_WINDOWS) {L"c:\\colon\\backslash", L"path", L"c:\\colon\\backslash\\path"}, {L"c:\\colon\\backslash\\", L"path", L"c:\\colon\\backslash\\path"}, {L"c:\\colon\\backslash\\\\", L"path", L"c:\\colon\\backslash\\\\path"}, @@ -130,12 +139,21 @@ const struct append_case { {L"c:\\colon\\backslash", L"", L"c:\\colon\\backslash\\"}, {L"", L"path", L"\\path"}, {L"", L"", L"\\"}, +#elif defined(OS_POSIX) + {L"/foo/bar", L"path", L"/foo/bar/path"}, + {L"/foo/bar/", L"path", L"/foo/bar/path"}, + {L"/foo/bar//", L"path", L"/foo/bar//path"}, + {L"/foo/bar/", L"", L"/foo/bar/"}, + {L"/foo/bar", L"", L"/foo/bar/"}, + {L"", L"path", L"/path"}, + {L"", L"", L"/"}, +#endif }; } // namespace TEST_F(FileUtilTest, AppendToPath) { - for (int i = 0; i < arraysize(append_cases); ++i) { + for (unsigned int i = 0; i < arraysize(append_cases); ++i) { const append_case& value = append_cases[i]; std::wstring result = value.path; file_util::AppendToPath(&result, value.ending); @@ -168,6 +186,7 @@ static const struct InsertBeforeExtensionCase { {L"foo", L".", L"foo."}, {L"foo.baz.dll", L"", L"foo.baz.dll"}, {L"foo.baz.dll", L".", L"foo.baz..dll"}, +#if defined(OS_WINDOWS) {L"\\", L"", L"\\"}, {L"\\", L"txt", L"\\txt"}, {L"\\.", L"txt", L"\\txt."}, @@ -180,10 +199,24 @@ static const struct InsertBeforeExtensionCase { {L"C:\\bar.baz\\foo.exe", L"", L"C:\\bar.baz\\foo.exe"}, {L"C:\\bar.baz\\foo.dll.exe", L"", L"C:\\bar.baz\\foo.dll.exe"}, {L"C:\\bar\\baz\\foo.exe", L" (1)", L"C:\\bar\\baz\\foo (1).exe"}, +#elif defined(OS_POSIX) + {L"/", L"", L"/"}, + {L"/", L"txt", L"/txt"}, + {L"/.", L"txt", L"/txt."}, + {L"/.", L"", L"/."}, + {L"/bar/foo.dll", L"txt", L"/bar/footxt.dll"}, + {L"/bar.baz/foodll", L"txt", L"/bar.baz/foodlltxt"}, + {L"/bar.baz/foo.dll", L"txt", L"/bar.baz/footxt.dll"}, + {L"/bar.baz/foo.dll.exe", L"txt", L"/bar.baz/foo.dlltxt.exe"}, + {L"/bar.baz/foo", L"", L"/bar.baz/foo"}, + {L"/bar.baz/foo.exe", L"", L"/bar.baz/foo.exe"}, + {L"/bar.baz/foo.dll.exe", L"", L"/bar.baz/foo.dll.exe"}, + {L"/bar/baz/foo.exe", L" (1)", L"/bar/baz/foo (1).exe"}, +#endif }; TEST_F(FileUtilTest, InsertBeforeExtensionTest) { - for (int i = 0; i < arraysize(kInsertBeforeExtension); ++i) { + for (unsigned int i = 0; i < arraysize(kInsertBeforeExtension); ++i) { std::wstring path(kInsertBeforeExtension[i].path); file_util::InsertBeforeExtension(&path, kInsertBeforeExtension[i].suffix); EXPECT_EQ(path, kInsertBeforeExtension[i].result); @@ -194,6 +227,7 @@ static const struct filename_case { const wchar_t* path; const wchar_t* filename; } filename_cases[] = { +#if defined(OS_WINDOWS) {L"c:\\colon\\backslash", L"backslash"}, {L"c:\\colon\\backslash\\", L""}, {L"\\\\filename.exe", L"filename.exe"}, @@ -204,10 +238,18 @@ static const struct filename_case { {L"c:/colon/backslash/", L""}, {L"//////", L""}, {L"///filename.exe", L"filename.exe"}, +#elif defined(OS_POSIX) + {L"/foo/bar", L"bar"}, + {L"/foo/bar/", L""}, + {L"/filename.exe", L"filename.exe"}, + {L"filename.exe", L"filename.exe"}, + {L"", L""}, + {L"/", L""}, +#endif }; TEST_F(FileUtilTest, GetFilenameFromPath) { - for (int i = 0; i < arraysize(filename_cases); ++i) { + for (unsigned int i = 0; i < arraysize(filename_cases); ++i) { const filename_case& value = filename_cases[i]; std::wstring result = file_util::GetFilenameFromPath(value.path); EXPECT_EQ(value.filename, result); @@ -219,16 +261,30 @@ static const struct extension_case { const wchar_t* path; const wchar_t* extension; } extension_cases[] = { +#if defined(OS_WINDOWS) {L"C:\\colon\\backslash\\filename.extension", L"extension"}, {L"C:\\colon\\backslash\\filename.", L""}, {L"C:\\colon\\backslash\\filename", L""}, {L"C:\\colon\\backslash\\", L""}, {L"C:\\colon\\backslash.\\", L""}, {L"C:\\colon\\backslash\filename.extension.extension2", L"extension2"}, +#elif defined(OS_POSIX) + {L"/foo/bar/filename.extension", L"extension"}, + {L"/foo/bar/filename.", L""}, + {L"/foo/bar/filename", L""}, + {L"/foo/bar/", L""}, + {L"/foo/bar./", L""}, + {L"/foo/bar/filename.extension.extension2", L"extension2"}, + {L".", L""}, + {L"..", L""}, + {L"./foo", L""}, + {L"./foo.extension", L"extension"}, + {L"/foo.extension1/bar.extension2", L"extension2"}, +#endif }; TEST_F(FileUtilTest, GetFileExtensionFromPath) { - for (int i = 0; i < arraysize(extension_cases); ++i) { + for (unsigned int i = 0; i < arraysize(extension_cases); ++i) { const extension_case& ext = extension_cases[i]; const std::wstring fext = file_util::GetFileExtensionFromPath(ext.path); EXPECT_EQ(ext.extension, fext); @@ -240,6 +296,7 @@ static const struct dir_case { const wchar_t* full_path; const wchar_t* directory; } dir_cases[] = { +#if defined(OS_WINDOWS) {L"C:\\WINDOWS\\system32\\gdi32.dll", L"C:\\WINDOWS\\system32"}, {L"C:\\WINDOWS\\system32\\not_exist_thx_1138", L"C:\\WINDOWS\\system32"}, {L"C:\\WINDOWS\\system32\\", L"C:\\WINDOWS\\system32"}, @@ -247,10 +304,21 @@ static const struct dir_case { {L"C:\\WINDOWS\\system32", L"C:\\WINDOWS"}, {L"C:\\WINDOWS\\system32.\\", L"C:\\WINDOWS\\system32."}, {L"C:\\", L"C:"}, +#elif defined(OS_POSIX) + {L"/foo/bar/gdi32.dll", L"/foo/bar"}, + {L"/foo/bar/not_exist_thx_1138", L"/foo/bar"}, + {L"/foo/bar/", L"/foo/bar"}, + {L"/foo/bar//", L"/foo/bar"}, + {L"/foo/bar", L"/foo"}, + {L"/foo/bar./", L"/foo/bar."}, + {L"/", L"/"}, + {L".", L"."}, + {L"..", L"."}, // yes, ".." technically lives in "." +#endif }; TEST_F(FileUtilTest, GetDirectoryFromPath) { - for (int i = 0; i < arraysize(dir_cases); ++i) { + for (unsigned int i = 0; i < arraysize(dir_cases); ++i) { const dir_case& dir = dir_cases[i]; const std::wstring parent = file_util::GetDirectoryFromPath(dir.full_path); @@ -258,6 +326,8 @@ TEST_F(FileUtilTest, GetDirectoryFromPath) { } } +// TODO(erikkay): implement +#if defined OS_WINDOWS TEST_F(FileUtilTest, CountFilesCreatedAfter) { // Create old file (that we don't want to count) std::wstring old_file_name = test_dir_; @@ -284,6 +354,7 @@ TEST_F(FileUtilTest, CountFilesCreatedAfter) { EXPECT_TRUE(file_util::Delete(new_file_name, false)); EXPECT_EQ(0, file_util::CountFilesCreatedAfter(test_dir_, test_start_time)); } +#endif // Tests that the Delete function works as expected, especially // the recursion flag. Also coincidentally tests PathExists. @@ -297,29 +368,32 @@ TEST_F(FileUtilTest, Delete) { std::wstring subdir_path = test_dir_; file_util::AppendToPath(&subdir_path, L"Subdirectory"); - CreateDirectory(subdir_path.c_str(), NULL); + file_util::CreateDirectory(subdir_path.c_str()); ASSERT_TRUE(file_util::PathExists(subdir_path)); std::wstring directory_contents = test_dir_; +#if defined(OS_WINDOWS) + // TODO(erikkay): see if anyone's actually using this feature of the API file_util::AppendToPath(&directory_contents, L"*"); // Delete non-recursively and check that only the file is deleted ASSERT_TRUE(file_util::Delete(directory_contents, false)); - ASSERT_FALSE(file_util::PathExists(file_name)); - ASSERT_TRUE(file_util::PathExists(subdir_path)); + EXPECT_FALSE(file_util::PathExists(file_name)); + EXPECT_TRUE(file_util::PathExists(subdir_path)); +#endif // Delete recursively and make sure all contents are deleted ASSERT_TRUE(file_util::Delete(directory_contents, true)); - ASSERT_FALSE(file_util::PathExists(file_name)); - ASSERT_FALSE(file_util::PathExists(subdir_path)); + EXPECT_FALSE(file_util::PathExists(file_name)); + EXPECT_FALSE(file_util::PathExists(subdir_path)); } TEST_F(FileUtilTest, Move) { // Create a directory std::wstring dir_name_from(test_dir_); file_util::AppendToPath(&dir_name_from, L"Move_From_Subdir"); - CreateDirectory(dir_name_from.c_str(), NULL); + file_util::CreateDirectory(dir_name_from.c_str()); ASSERT_TRUE(file_util::PathExists(dir_name_from)); // Create a file under the directory @@ -345,11 +419,13 @@ TEST_F(FileUtilTest, Move) { EXPECT_TRUE(file_util::PathExists(file_name_to)); } +// TODO(erikkay): implement +#if defined(OS_WINDOWS) TEST_F(FileUtilTest, CopyDirectoryRecursively) { // Create a directory. std::wstring dir_name_from(test_dir_); file_util::AppendToPath(&dir_name_from, L"Copy_From_Subdir"); - CreateDirectory(dir_name_from.c_str(), NULL); + file_util::CreateDirectory(dir_name_from.c_str()); ASSERT_TRUE(file_util::PathExists(dir_name_from)); // Create a file under the directory. @@ -361,7 +437,7 @@ TEST_F(FileUtilTest, CopyDirectoryRecursively) { // Create a subdirectory. std::wstring subdir_name_from(dir_name_from); file_util::AppendToPath(&subdir_name_from, L"Subdir"); - CreateDirectory(subdir_name_from.c_str(), NULL); + file_util::CreateDirectory(subdir_name_from.c_str()); ASSERT_TRUE(file_util::PathExists(subdir_name_from)); // Create a file under the subdirectory. @@ -399,7 +475,7 @@ TEST_F(FileUtilTest, CopyDirectory) { // Create a directory. std::wstring dir_name_from(test_dir_); file_util::AppendToPath(&dir_name_from, L"Copy_From_Subdir"); - CreateDirectory(dir_name_from.c_str(), NULL); + file_util::CreateDirectory(dir_name_from.c_str()); ASSERT_TRUE(file_util::PathExists(dir_name_from)); // Create a file under the directory. @@ -411,7 +487,7 @@ TEST_F(FileUtilTest, CopyDirectory) { // Create a subdirectory. std::wstring subdir_name_from(dir_name_from); file_util::AppendToPath(&subdir_name_from, L"Subdir"); - CreateDirectory(subdir_name_from.c_str(), NULL); + file_util::CreateDirectory(subdir_name_from.c_str()); ASSERT_TRUE(file_util::PathExists(subdir_name_from)); // Create a file under the subdirectory. @@ -441,12 +517,13 @@ TEST_F(FileUtilTest, CopyDirectory) { EXPECT_TRUE(file_util::PathExists(file_name_to)); EXPECT_FALSE(file_util::PathExists(subdir_name_to)); } +#endif TEST_F(FileUtilTest, CopyFile) { // Create a directory std::wstring dir_name_from(test_dir_); file_util::AppendToPath(&dir_name_from, L"Copy_From_Subdir"); - CreateDirectory(dir_name_from.c_str(), NULL); + file_util::CreateDirectory(dir_name_from.c_str()); ASSERT_TRUE(file_util::PathExists(dir_name_from)); // Create a file under the directory @@ -460,14 +537,27 @@ TEST_F(FileUtilTest, CopyFile) { std::wstring dest_file(dir_name_from); file_util::AppendToPath(&dest_file, L"DestFile.txt"); ASSERT_TRUE(file_util::CopyFile(file_name_from, dest_file)); + + // Copy the file to another location using '..' in the path. + std::wstring dest_file2(dir_name_from); + file_util::AppendToPath(&dest_file2, L".."); + file_util::AppendToPath(&dest_file2, L"DestFile.txt"); + ASSERT_TRUE(file_util::CopyFile(file_name_from, dest_file2)); + std::wstring dest_file2_test(dir_name_from); + file_util::UpOneDirectory(&dest_file2_test); + file_util::AppendToPath(&dest_file2_test, L"DestFile.txt"); // Check everything has been copied. EXPECT_TRUE(file_util::PathExists(file_name_from)); EXPECT_TRUE(file_util::PathExists(dest_file)); const std::wstring read_contents = ReadTextFile(dest_file); EXPECT_EQ(file_contents, read_contents); + EXPECT_TRUE(file_util::PathExists(dest_file2_test)); + EXPECT_TRUE(file_util::PathExists(dest_file2)); } +// TODO(erikkay): implement +#if defined(OS_WINDOWS) TEST_F(FileUtilTest, GetFileCreationLocalTime) { std::wstring file_name = test_dir_; file_util::AppendToPath(&file_name, L"Test File.txt"); @@ -500,6 +590,7 @@ TEST_F(FileUtilTest, GetFileCreationLocalTime) { ASSERT_TRUE(DeleteFile(file_name.c_str())); } +#endif typedef testing::Test ReadOnlyFileUtilTest; @@ -550,6 +641,8 @@ TEST(ReadOnlyFileUtilTest, ContentsEqual) { EXPECT_FALSE(file_util::ContentsEqual(binary_file, binary_file_diff)); } +// We don't need equivalent functionality outside of Windows. +#if defined(OS_WINDOWS) TEST_F(FileUtilTest, ResolveShortcutTest) { std::wstring target_file = test_dir_; file_util::AppendToPath(&target_file, L"Target.txt"); @@ -617,25 +710,32 @@ TEST_F(FileUtilTest, CreateShortcutTest) { DeleteFile(link_file.c_str()); CoUninitialize(); } +#endif TEST_F(FileUtilTest, CreateTemporaryFileNameTest) { std::wstring temp_file; file_util::CreateTemporaryFileName(&temp_file); EXPECT_EQ(file_util::PathExists(temp_file), true); + EXPECT_EQ(file_util::Delete(temp_file, false), true); } TEST_F(FileUtilTest, CreateNewTempDirectoryTest) { std::wstring temp_dir; file_util::CreateNewTempDirectory(std::wstring(), &temp_dir); EXPECT_EQ(file_util::PathExists(temp_dir), true); + EXPECT_EQ(file_util::Delete(temp_dir, false), true); } TEST_F(FileUtilTest, CreateDirectoryTest) { std::wstring test_root = test_dir_; file_util::AppendToPath(&test_root, L"create_directory_test"); std::wstring test_path(test_root); +#if defined(OS_WINDOWS) file_util::AppendToPath(&test_path, L"dir\\tree\\likely\\doesnt\\exist\\"); - +#elif defined(OS_POSIX) + file_util::AppendToPath(&test_path, L"dir/tree/likely/doesnt/exist/"); +#endif + EXPECT_EQ(file_util::PathExists(test_path), false); EXPECT_EQ(file_util::CreateDirectory(test_path), true); EXPECT_EQ(file_util::PathExists(test_path), true); @@ -644,18 +744,24 @@ TEST_F(FileUtilTest, CreateDirectoryTest) { EXPECT_EQ(file_util::PathExists(test_path), false); } -static const struct { +static const struct goodbad_pair { std::wstring bad_name; std::wstring good_name; } kIllegalCharacterCases[] = { {L"bad*file:name?.jpg", L"bad-file-name-.jpg"}, {L"**********::::.txt", L"--------------.txt"}, - {L"bad*file\\name.jpg", L"bad-file-name.jpg"}, // We can't use UCNs (universal character names) for C0/C1 characters and // U+007F, but \x escape is interpreted by MSVC and gcc as we intend. {L"bad\x0003\x0091 file\u200E\u200Fname.png", L"bad-- file--name.png"}, +#if defined(OS_WINDOWS) + {L"bad*file\\name.jpg", L"bad-file-name.jpg"}, {L"\t bad*file\\name/.jpg ", L"bad-file-name-.jpg"}, {L"bad\uFFFFfile\U0010FFFEname.jpg ", L"bad-file-name.jpg"}, +#elif defined(OS_POSIX) + {L"bad*file?name.jpg", L"bad-file-name.jpg"}, + {L"\t bad*file?name/.jpg ", L"bad-file-name-.jpg"}, + {L"bad\uFFFFfile-name.jpg ", L"bad-file-name.jpg"}, +#endif {L"this_file_name is okay!.mp3", L"this_file_name is okay!.mp3"}, {L"\u4E00\uAC00.mp3", L"\u4E00\uAC00.mp3"}, {L"\u0635\u200C\u0644.mp3", L"\u0635\u200C\u0644.mp3"}, @@ -665,7 +771,7 @@ static const struct { }; TEST_F(FileUtilTest, ReplaceIllegalCharactersTest) { - for (int i = 0; i < arraysize(kIllegalCharacterCases); ++i) { + for (unsigned int i = 0; i < arraysize(kIllegalCharacterCases); ++i) { std::wstring bad_name(kIllegalCharacterCases[i].bad_name); file_util::ReplaceIllegalCharacters(&bad_name, L'-'); EXPECT_EQ(kIllegalCharacterCases[i].good_name, bad_name); @@ -696,7 +802,7 @@ static const struct ReplaceExtensionCase { }; TEST_F(FileUtilTest, ReplaceExtensionTest) { - for (int i = 0; i < arraysize(kReplaceExtension); ++i) { + for (unsigned int i = 0; i < arraysize(kReplaceExtension); ++i) { std::wstring file_name(kReplaceExtension[i].file_name); file_util::ReplaceExtension(&file_name, kReplaceExtension[i].extension); EXPECT_EQ(file_name, kReplaceExtension[i].result); @@ -710,63 +816,86 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_EQ(f0.Next(), L""); EXPECT_EQ(f0.Next(), L""); - // Populate the test dir. - file_util::CreateDirectory(test_dir_ + L"\\dir1"); - file_util::CreateDirectory(test_dir_ + L"\\dir2"); - CreateTextFile(test_dir_ + L"\\dir2\\dir2file.txt", L""); - file_util::CreateDirectory(test_dir_ + L"\\dir2\\inner"); - CreateTextFile(test_dir_ + L"\\dir2\\inner\\innerfile.txt", L""); - CreateTextFile(test_dir_ + L"\\file1.txt", L""); - CreateTextFile(test_dir_ + L"\\file2.txt", L""); + // create the directories + std::wstring dir1 = test_dir_; + file_util::AppendToPath(&dir1, L"dir1"); + EXPECT_TRUE(file_util::CreateDirectory(dir1)); + std::wstring dir2 = test_dir_; + file_util::AppendToPath(&dir2, L"dir2"); + EXPECT_TRUE(file_util::CreateDirectory(dir2)); + std::wstring dir2inner = dir2; + file_util::AppendToPath(&dir2inner, L"inner"); + EXPECT_TRUE(file_util::CreateDirectory(dir2inner)); + + // create the files + std::wstring dir2file = dir2; + file_util::AppendToPath(&dir2file, L"dir2file.txt"); + CreateTextFile(dir2file, L""); + std::wstring dir2innerfile = dir2inner; + file_util::AppendToPath(&dir2innerfile, L"innerfile.txt"); + CreateTextFile(dir2innerfile, L""); + std::wstring file1 = test_dir_; + file_util::AppendToPath(&file1, L"file1.txt"); + CreateTextFile(file1, L""); + std::wstring file2_rel = dir2; + file_util::AppendToPath(&file2_rel, L".."); + file_util::AppendToPath(&file2_rel, L"file2.txt"); + CreateTextFile(file2_rel, L""); + std::wstring file2_abs = test_dir_; + file_util::AppendToPath(&file2_abs, L"file2.txt"); // Only enumerate files. file_util::FileEnumerator f1(test_dir_, true, file_util::FileEnumerator::FILES); FindResultCollector c1(f1); - EXPECT_TRUE(c1.HasFile(test_dir_ + L"\\file1.txt")); - EXPECT_TRUE(c1.HasFile(test_dir_ + L"\\file2.txt")); - EXPECT_TRUE(c1.HasFile(test_dir_ + L"\\dir2\\dir2file.txt")); - EXPECT_TRUE(c1.HasFile(test_dir_ + L"\\dir2\\inner\\innerfile.txt")); + EXPECT_TRUE(c1.HasFile(file1)); + EXPECT_TRUE(c1.HasFile(file2_abs)); + EXPECT_TRUE(c1.HasFile(dir2file)); + EXPECT_TRUE(c1.HasFile(dir2innerfile)); + EXPECT_EQ(c1.size(), 4); // Only enumerate directories. file_util::FileEnumerator f2(test_dir_, true, file_util::FileEnumerator::DIRECTORIES); FindResultCollector c2(f2); - EXPECT_TRUE(c2.HasFile(test_dir_ + L"\\dir1")); - EXPECT_TRUE(c2.HasFile(test_dir_ + L"\\dir2")); - EXPECT_TRUE(c2.HasFile(test_dir_ + L"\\dir2\\inner")); + EXPECT_TRUE(c2.HasFile(dir1)); + EXPECT_TRUE(c2.HasFile(dir2)); + EXPECT_TRUE(c2.HasFile(dir2inner)); + EXPECT_EQ(c2.size(), 3); // Enumerate files and directories. file_util::FileEnumerator f3(test_dir_, true, file_util::FileEnumerator::FILES_AND_DIRECTORIES); FindResultCollector c3(f3); - EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir1")); - EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2")); - EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\file1.txt")); - EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\file2.txt")); - EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2\\dir2file.txt")); - EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2\\dir2file.txt")); - EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2\\inner")); - EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2\\inner\\innerfile.txt")); + EXPECT_TRUE(c3.HasFile(dir1)); + EXPECT_TRUE(c3.HasFile(dir2)); + EXPECT_TRUE(c3.HasFile(file1)); + EXPECT_TRUE(c3.HasFile(file2_abs)); + EXPECT_TRUE(c3.HasFile(dir2file)); + EXPECT_TRUE(c3.HasFile(dir2inner)); + EXPECT_TRUE(c3.HasFile(dir2innerfile)); + EXPECT_EQ(c3.size(), 7); // Non-recursive operation. file_util::FileEnumerator f4(test_dir_, false, file_util::FileEnumerator::FILES_AND_DIRECTORIES); FindResultCollector c4(f4); - EXPECT_TRUE(c4.HasFile(test_dir_ + L"\\dir1")); - EXPECT_TRUE(c4.HasFile(test_dir_ + L"\\dir2")); - EXPECT_TRUE(c4.HasFile(test_dir_ + L"\\file1.txt")); - EXPECT_TRUE(c4.HasFile(test_dir_ + L"\\file2.txt")); + EXPECT_TRUE(c4.HasFile(dir2)); + EXPECT_TRUE(c4.HasFile(dir2)); + EXPECT_TRUE(c4.HasFile(file1)); + EXPECT_TRUE(c4.HasFile(file2_abs)); + EXPECT_EQ(c4.size(), 4); // Enumerate with a pattern. file_util::FileEnumerator f5(test_dir_, true, file_util::FileEnumerator::FILES_AND_DIRECTORIES, L"dir*"); FindResultCollector c5(f5); - EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir1")); - EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir2")); - EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir2\\dir2file.txt")); - EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir2\\inner")); - EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir2\\inner\\innerfile.txt")); + EXPECT_TRUE(c5.HasFile(dir1)); + EXPECT_TRUE(c5.HasFile(dir2)); + EXPECT_TRUE(c5.HasFile(dir2file)); + EXPECT_TRUE(c5.HasFile(dir2inner)); + EXPECT_TRUE(c5.HasFile(dir2innerfile)); + EXPECT_EQ(c5.size(), 5); // Make sure the destructor closes the find handle while in the middle of a // query to allow TearDown to delete the directory. |