summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorerikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-15 17:32:10 +0000
committererikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-15 17:32:10 +0000
commit37088fefa67704ad6f18e1e72e2e6292ba48ee1b (patch)
treeef54e2c068b1a63dfe29b01fb59af6e1148d16d3
parente87ce0ff4a37f8e5bd0e4d7938bcf93b66d29ec9 (diff)
downloadchromium_src-37088fefa67704ad6f18e1e72e2e6292ba48ee1b.zip
chromium_src-37088fefa67704ad6f18e1e72e2e6292ba48ee1b.tar.gz
chromium_src-37088fefa67704ad6f18e1e72e2e6292ba48ee1b.tar.bz2
Part two of file_util porting. Almost all of the functionality has been ported, including the unit tests now. Some of this API isn't great, and should be cleaned up, but I'd like to hold off and do that in a followup changelist. More general code cleanup is likely needed here as well.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@944 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/base.xcodeproj/project.pbxproj2
-rw-r--r--base/base_paths.cc7
-rw-r--r--base/base_paths.h3
-rw-r--r--base/base_paths_mac.h10
-rw-r--r--base/base_paths_mac.mm10
-rw-r--r--base/base_paths_win.cc7
-rw-r--r--base/base_paths_win.h3
-rw-r--r--base/file_util.cc40
-rw-r--r--base/file_util.h38
-rw-r--r--base/file_util_posix.cc219
-rw-r--r--base/file_util_unittest.cc247
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.