diff options
Diffstat (limited to 'base')
-rw-r--r-- | base/SConscript | 2 | ||||
-rw-r--r-- | base/base.xcodeproj/project.pbxproj | 20 | ||||
-rw-r--r-- | base/build/base.vcproj | 8 | ||||
-rw-r--r-- | base/build/base_unittests.vcproj | 4 | ||||
-rw-r--r-- | base/file_path.cc | 175 | ||||
-rw-r--r-- | base/file_path.h | 173 | ||||
-rw-r--r-- | base/file_path_unittest.cc | 348 |
7 files changed, 725 insertions, 5 deletions
diff --git a/base/SConscript b/base/SConscript index 41693a5..79e11b7 100644 --- a/base/SConscript +++ b/base/SConscript @@ -35,6 +35,7 @@ input_files = [ 'bzip2_error_handler.cc', 'command_line.cc', 'debug_util.cc', + 'file_path.cc', 'file_util.cc', 'histogram.cc', 'icu_util.cc', @@ -253,6 +254,7 @@ test_files = [ 'atomicops_unittest.cc', 'command_line_unittest.cc', 'condition_variable_unittest.cc', + 'file_path_unittest.cc', 'file_util_unittest.cc', 'hmac_unittest.cc', 'histogram_unittest.cc', diff --git a/base/base.xcodeproj/project.pbxproj b/base/base.xcodeproj/project.pbxproj index 09a6eba..3d8832f 100644 --- a/base/base.xcodeproj/project.pbxproj +++ b/base/base.xcodeproj/project.pbxproj @@ -38,6 +38,8 @@ 4D11B59A0E91730200EF7617 /* rand_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D11B5940E9172F800EF7617 /* rand_util.cc */; }; 4D11B59B0E91730200EF7617 /* rand_util_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D11B5960E9172F800EF7617 /* rand_util_posix.cc */; }; 4D11B59C0E91730500EF7617 /* rand_util_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D11B5970E9172F800EF7617 /* rand_util_unittest.cc */; }; + 4D11B89E0E929F0400EF7617 /* file_path.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D11B89B0E929EFF00EF7617 /* file_path.cc */; }; + 4D11B89F0E929F0700EF7617 /* file_path_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D11B89D0E929EFF00EF7617 /* file_path_unittest.cc */; }; 7B26302F0E82F218001CE27F /* message_pump_libevent.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7B26302D0E82F218001CE27F /* message_pump_libevent.cc */; }; 7B2630330E82F258001CE27F /* libevent.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B2630240E82F1E6001CE27F /* libevent.a */; }; 7B4C5F4A0E4B6BF900679E8F /* sys_string_conversions_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7B4C5F480E4B6BF900679E8F /* sys_string_conversions_mac.cc */; }; @@ -368,6 +370,9 @@ 4D11B5950E9172F800EF7617 /* rand_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rand_util.h; sourceTree = "<group>"; }; 4D11B5960E9172F800EF7617 /* rand_util_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rand_util_posix.cc; sourceTree = "<group>"; }; 4D11B5970E9172F800EF7617 /* rand_util_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rand_util_unittest.cc; sourceTree = "<group>"; }; + 4D11B89B0E929EFF00EF7617 /* file_path.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_path.cc; sourceTree = "<group>"; }; + 4D11B89C0E929EFF00EF7617 /* file_path.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_path.h; sourceTree = "<group>"; }; + 4D11B89D0E929EFF00EF7617 /* file_path_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_path_unittest.cc; sourceTree = "<group>"; }; 7B1435DE0E78416400901940 /* skia_utils_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = skia_utils_mac.h; sourceTree = "<group>"; }; 7B1435DF0E78419700901940 /* native_widget_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = native_widget_types.h; sourceTree = "<group>"; }; 7B26301F0E82F1E6001CE27F /* libevent.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libevent.xcodeproj; path = third_party/libevent/libevent.xcodeproj; sourceTree = "<group>"; }; @@ -786,14 +791,14 @@ isa = PBXGroup; children = ( 7B836C4B0E55C69000F6AD31 /* Configuration */, - 825402B60D92D0E20006B936 /* base */, + 825402B60D92D0E20006B936 /* Source */, 829E2FA80DBFD7D500819EBF /* Frameworks */, 7B78D40F0E54FE8D00609465 /* Projects */, 825402BC0D92D0FA0006B936 /* Products */, ); sourceTree = "<group>"; }; - 825402B60D92D0E20006B936 /* base */ = { + 825402B60D92D0E20006B936 /* Source */ = { isa = PBXGroup; children = ( 825403B40D92D2EC0006B936 /* gfx */, @@ -839,6 +844,9 @@ 7B78CE100E53131800609465 /* debug_util_posix.cc */, 825402FF0D92D1BC0006B936 /* event_recorder.cc */, 825403000D92D1BC0006B936 /* event_recorder.h */, + 4D11B89B0E929EFF00EF7617 /* file_path.cc */, + 4D11B89C0E929EFF00EF7617 /* file_path.h */, + 4D11B89D0E929EFF00EF7617 /* file_path_unittest.cc */, A5A026540E4A214600498DA9 /* file_util.cc */, 825403030D92D1C50006B936 /* file_util.h */, A5A0270A0E4A630D00498DA9 /* file_util_mac.mm */, @@ -986,9 +994,9 @@ 7B6AF6330E80211700F9F9CF /* sys_info_unittest.cc */, 7B4C5F470E4B6BF900679E8F /* sys_string_conversions.h */, 7B4C5F480E4B6BF900679E8F /* sys_string_conversions_mac.cc */, - E4CE9D760E8C1FCA00D5378C /* system_monitor_unittest.cc */, - E4CE9D770E8C1FCA00D5378C /* system_monitor.h */, E4CE9D780E8C1FCA00D5378C /* system_monitor.cc */, + E4CE9D770E8C1FCA00D5378C /* system_monitor.h */, + E4CE9D760E8C1FCA00D5378C /* system_monitor_unittest.cc */, 8254037E0D92D2CF0006B936 /* task.h */, 93E703230E5D64F00046259B /* thread.cc */, 825403800D92D2CF0006B936 /* thread.h */, @@ -1032,7 +1040,7 @@ 7BF1658B0E663B3500AA999E /* worker_pool_mac.mm */, 7BF1658C0E663B3500AA999E /* worker_pool_unittest.cc */, ); - name = base; + name = Source; sourceTree = "<group>"; }; 825402BC0D92D0FA0006B936 /* Products */ = { @@ -1335,6 +1343,7 @@ 824653680DC12CEC007C2BAA /* condition_variable_posix.cc in Sources */, 7B78CE250E5314A000609465 /* debug_util.cc in Sources */, 7B78CE120E53131800609465 /* debug_util_posix.cc in Sources */, + 4D11B89E0E929F0400EF7617 /* file_path.cc in Sources */, A5A026550E4A214600498DA9 /* file_util.cc in Sources */, A5A0270B0E4A630D00498DA9 /* file_util_mac.mm in Sources */, A5A0268E0E4A2BDC00498DA9 /* file_util_posix.cc in Sources */, @@ -1424,6 +1433,7 @@ 7B78D38F0E54FE0100609465 /* command_line_unittest.cc in Sources */, BA73AA330E5F614B00A20026 /* condition_variable_unittest.cc in Sources */, 7B8505D40E5B43FE00730B43 /* convolver_unittest.cc in Sources */, + 4D11B89F0E929F0700EF7617 /* file_path_unittest.cc in Sources */, A5CE1D2B0E55F4D800AD0606 /* file_util_unittest.cc in Sources */, 7B78D3910E54FE0100609465 /* file_version_info_unittest.cc in Sources */, 93611B1A0E5A878400F9405D /* histogram_unittest.cc in Sources */, diff --git a/base/build/base.vcproj b/base/build/base.vcproj index a5cd362..bbcfffc 100644 --- a/base/build/base.vcproj +++ b/base/build/base.vcproj @@ -278,6 +278,14 @@ > </File> <File + RelativePath="..\file_path.cc" + > + </File> + <File + RelativePath="..\file_path.h" + > + </File> + <File RelativePath="..\file_util.cc" > </File> diff --git a/base/build/base_unittests.vcproj b/base/build/base_unittests.vcproj index daf0360..50b14cc 100644 --- a/base/build/base_unittests.vcproj +++ b/base/build/base_unittests.vcproj @@ -184,6 +184,10 @@ > </File> <File + RelativePath="..\file_path_unittest.cc" + > + </File> + <File RelativePath="..\file_util_unittest.cc" > </File> diff --git a/base/file_path.cc b/base/file_path.cc new file mode 100644 index 0000000..b20eb6b --- /dev/null +++ b/base/file_path.cc @@ -0,0 +1,175 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_path.h" + +#if defined(FILE_PATH_USES_WIN_SEPARATORS) +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/"); +#else // FILE_PATH_USES_WIN_SEPARATORS +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/"); +#endif // FILE_PATH_USES_WIN_SEPARATORS + +const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL("."); +const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL(".."); + +// Returns true if |character| is in kSeparators. +static bool IsSeparator(FilePath::CharType character) { + for (size_t i = 0; i < arraysize(FilePath::kSeparators) - 1; ++i) { + if (character == FilePath::kSeparators[i]) { + return true; + } + } + + return false; +} + +// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't +// guaranteed to not modify their input strings, and in fact are implmeneted +// differently in this regard on different platforms. Don't use them, but +// adhere to their behavior. +FilePath FilePath::DirName() const { + FilePath new_path(path_); + new_path.StripTrailingSeparators(); + + // The drive letter, if any, always needs to remain in the output. If there + // is no drive letter, as will always be the case on platforms which do not + // support drive letters, letter will be npos, or -1, so the comparisons and + // resizes below using letter will still be valid. + StringType::size_type letter = new_path.FindDriveLetter(); + + StringType::size_type last_separator = + new_path.path_.find_last_of(kSeparators, StringType::npos, + arraysize(kSeparators) - 1); + if (last_separator == StringType::npos) { + // path_ is in the current directory. + new_path.path_.resize(letter + 1); + } else if (last_separator == letter + 1) { + // path_ is in the root directory. + new_path.path_.resize(letter + 2); + } else if (last_separator == letter + 2 && + IsSeparator(new_path.path_[letter + 1])) { + // path_ is in "//" (possibly with a drive letter); leave the double + // separator intact indicating alternate root. + new_path.path_.resize(letter + 3); + } else if (last_separator != 0) { + // path_ is somewhere else, trim the basename. + new_path.path_.resize(last_separator); + } + + new_path.StripTrailingSeparators(); + if (!new_path.path_.length()) + new_path.path_ = kCurrentDirectory; + + return new_path; +} + +FilePath FilePath::BaseName() const { + FilePath new_path(path_); + new_path.StripTrailingSeparators(); + + // The drive letter, if any, is always stripped. + StringType::size_type letter = new_path.FindDriveLetter(); + if (letter != StringType::npos) { + new_path.path_.erase(0, letter + 1); + } + + // Keep everything after the final separator, but if the pathname is only + // one character and it's a separator, leave it alone. + StringType::size_type last_separator = + new_path.path_.find_last_of(kSeparators, StringType::npos, + arraysize(kSeparators) - 1); + if (last_separator != StringType::npos && + last_separator < new_path.path_.length() - 1) { + new_path.path_.erase(0, last_separator + 1); + } + + return new_path; +} + +FilePath FilePath::Append(const FilePath::StringType& component) const { + if (path_.compare(kCurrentDirectory) == 0) { + // Append normally doesn't do any normalization, but as a special case, + // when appending to kCurrentDirectory, just return a new path for the + // component argument. Appending component to kCurrentDirectory would + // serve no purpose other than needlessly lengthening the path, and + // it's likely in practice to wind up with FilePath objects containing + // only kCurrentDirectory when calling DirName on a single relative path + // component. + return FilePath(component); + } + + FilePath new_path(path_); + new_path.StripTrailingSeparators(); + + // Don't append a separator if the path is empty (indicating the current + // directory) or if the path component is empty (indicating nothing to + // append). + if (component.length() > 0 && new_path.path_.length() > 0) { + + // Don't append a separator if the path still ends with a trailing + // separator after stripping (indicating the root directory). + if (!IsSeparator(new_path.path_[new_path.path_.length() - 1])) { + + // Don't append a separator if the path is just a drive letter. + if (new_path.FindDriveLetter() + 1 != new_path.path_.length()) { + new_path.path_.append(1, kSeparators[0]); + } + } + } + + new_path.path_.append(component); + return new_path; +} + +FilePath::StringType::size_type FilePath::FindDriveLetter() const { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + // This is dependent on an ASCII-based character set, but that's a + // reasonable assumption. iswalpha can be too inclusive here. + if (path_.length() >= 2 && path_[1] == L':' && + ((path_[0] >= L'A' && path_[0] <= L'Z') || + (path_[0] >= L'a' && path_[0] <= L'z'))) { + return 1; + } + return StringType::npos; +#else // FILE_PATH_USES_DRIVE_LETTERS + return StringType::npos; +#endif // FILE_PATH_USES_DRIVE_LETTERS +} + +bool FilePath::IsAbsolute() const { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + StringType::size_type letter = FindDriveLetter(); + if (letter != StringType::npos) { + // Look for a separator right after the drive specification. + return path_.length() > letter + 1 && IsSeparator(path_[letter + 1]); + } + // Look for a pair of leading separators. + return path_.length() > 1 && IsSeparator(path_[0]) && IsSeparator(path_[1]); +#else // FILE_PATH_USES_DRIVE_LETTERS + // Look for a separator in the first position. + return path_.length() > 0 && IsSeparator(path_[0]); +#endif // FILE_PATH_USES_DRIVE_LETTERS +} + +void FilePath::StripTrailingSeparators() { + // If there is no drive letter, start will be 1, which will prevent stripping + // the leading separator if there is only one separator. If there is a drive + // letter, start will be set appropriately to prevent stripping the first + // separator following the drive letter, if a separator immediately follows + // the drive letter. + StringType::size_type start = FindDriveLetter() + 2; + + StringType::size_type last_stripped = StringType::npos; + for (StringType::size_type pos = path_.length(); + pos > start && IsSeparator(path_[pos - 1]); + --pos) { + // If the string only has two separators and they're at the beginning, + // don't strip them, unless the string began with more than two separators. + if (pos != start + 1 || last_stripped == start + 2 || + !IsSeparator(path_[start - 1])) { + path_.resize(pos - 1); + last_stripped = pos; + } + } +} diff --git a/base/file_path.h b/base/file_path.h new file mode 100644 index 0000000..cb46f36 --- /dev/null +++ b/base/file_path.h @@ -0,0 +1,173 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// FilePath is a container for pathnames stored in a platform's native string +// type, providing containers for manipulation in according with the +// platform's conventions for pathnames. It supports the following path +// types: +// +// POSIX Windows +// --------------- ---------------------------------- +// Fundamental type char[] wchar_t[] +// Encoding unspecified* UTF-16 +// Separator / \, tolerant of / +// Drive letters no case-insensitive A-Z followed by : +// Alternate root // (surprise!) \\, for UNC paths +// +// * The encoding need not be specified on POSIX systems, although some +// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8. +// Linux does not specify an encoding, but in practice, the locale's +// character set may be used. +// +// FilePath objects are intended to be used anywhere paths are. An +// application may pass FilePath objects around internally, masking the +// underlying differences between systems, only differing in implementation +// where interfacing directly with the system. For example, a single +// OpenFile(const FilePath &) function may be made available, allowing all +// callers to operate without regard to the underlying implementation. On +// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might +// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This +// allows each platform to pass pathnames around without requiring conversions +// between encodings, which has an impact on performance, but more imporantly, +// has an impact on correctness on platforms that do not have well-defined +// encodings for pathnames. +// +// Several methods are available to perform common operations on a FilePath +// object, such as determining the parent directory (DirName), isolating the +// final path component (BaseName), and appending a relative pathname string +// to an existing FilePath object (Append). These methods are highly +// recommended over attempting to split and concatenate strings directly. +// These methods are based purely on string manipulation and knowledge of +// platform-specific pathname conventions, and do not consult the filesystem +// at all, making them safe to use without fear of blocking on I/O operations. +// These methods do not function as mutators but instead return distinct +// instances of FilePath objects, and are therefore safe to use on const +// objects. The objects themselves are safe to share between threads. +// +// To aid in initialization of FilePath objects from string literals, a +// FILE_PATH_LITERAL macro is provided, which accounts for the difference +// between char[]-based pathnames on POSIX systems and wchar_t[]-based +// pathnames on Windows. +// +// Because a FilePath object should not be instantiated at the global scope, +// instead, use a FilePath::CharType[] and initialize it with +// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the +// character array. Example: +// +// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt"); +// | +// | void Function() { +// | FilePath log_file_path(kLogFileName); +// | [...] +// | } + +#ifndef BASE_FILE_PATH_H_ +#define BASE_FILE_PATH_H_ + +#include <string> + +#include "base/basictypes.h" + +// Windows-style drive letter support and pathname separator characters can be +// enabled and disabled independently, to aid testing. These #defines are +// here so that the same setting can be used in both the implementation and +// in the unit test. +#if defined(OS_WIN) +#define FILE_PATH_USES_DRIVE_LETTERS +#define FILE_PATH_USES_WIN_SEPARATORS +#endif // OS_WIN + +// An abstraction to isolate users from the differences between native +// pathnames on different platforms. +class FilePath { + public: +#if defined(OS_POSIX) + // On most platforms, native pathnames are char arrays, and the encoding + // may or may not be specified. On Mac OS X, native pathnames are encoded + // in UTF-8. + typedef std::string StringType; +#elif defined(OS_WIN) + // On Windows, for Unicode-aware applications, native pathnames are wchar_t + // arrays encoded in UTF-16. + typedef std::wstring StringType; +#endif // OS_WIN + + typedef StringType::value_type CharType; + + // Null-terminated array of separators used to separate components in + // hierarchical paths. Each character in this array is a valid separator, + // but kSeparators[0] is treated as the canonical separator and will be used + // when composing pathnames. + static const CharType kSeparators[]; + + // A special path component meaning "this directory." + static const CharType kCurrentDirectory[]; + + // A special path component meaning "the parent directory." + static const CharType kParentDirectory[]; + + FilePath() {} + FilePath(const FilePath& that) : path_(that.path_) {} + explicit FilePath(const StringType& path) : path_(path) {} + + FilePath& operator=(const FilePath& that) { + path_ = that.path_; + return *this; + } + + const StringType& value() const { return path_; } + + // Returns a FilePath corresponding to the directory containing the path + // named by this object, stripping away the file component. If this object + // only contains one component, returns a FilePath identifying + // kCurrentDirectory. If this object already refers to the root directory, + // returns a FilePath identifying the root directory. + FilePath DirName() const; + + // Returns a FilePath corresponding to the last path component of this + // object, either a file or a directory. If this object already refers to + // the root directory, returns a FilePath identifying the root directory; + // this is the only situation in which BaseName will return an absolute path. + FilePath BaseName() const; + + // Returns a FilePath by appending a separator and the supplied path + // component to this object's path. Append takes care to avoid adding + // excessive separators if this object's path already ends with a separator. + // If this object's path is kCurrentDirectory, a new FilePath corresponding + // only to |component| is returned. |component| must be a relative path; + // it is an error to pass an absolute path. + FilePath Append(const StringType& component) const; + + // Returns true if this FilePath contains an absolute path. On Windows, an + // absolute path begins with either a drive letter specification followed by + // a separator character, or with two separator characters. On POSIX + // platforms, an absolute path begins with a separator character. + bool IsAbsolute() const; + + private: + // If this FilePath contains a drive letter specification, returns the + // position of the last character of the drive letter specification, + // otherwise returns npos. This can only be true on Windows, when a pathname + // begins with a letter followed by a colon. On other platforms, this always + // returns npos. + StringType::size_type FindDriveLetter() const; + + // Remove trailing separators from this object. If the path is absolute, it + // will never be stripped any more than to refer to the absolute root + // directory, so "////" will become "/", not "". A leading pair of + // separators is never stripped, to support alternate roots. This is used to + // support UNC paths on Windows. + void StripTrailingSeparators(); + + StringType path_; +}; + +// Macros for string literal initialization of FilePath::CharType[]. +#if defined(OS_POSIX) +#define FILE_PATH_LITERAL(x) x +#elif defined(OS_WIN) +#define FILE_PATH_LITERAL(x) L ## x +#endif // OS_WIN + +#endif // BASE_FILE_PATH_H_ diff --git a/base/file_path_unittest.cc b/base/file_path_unittest.cc new file mode 100644 index 0000000..0255a76 --- /dev/null +++ b/base/file_path_unittest.cc @@ -0,0 +1,348 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_path.h" + +#include "base/basictypes.h" +#include "gtest/gtest.h" + +// This macro helps avoid wrapped lines in the test structs. +#define FPL(x) FILE_PATH_LITERAL(x) + +struct UnaryTestData { + const FilePath::CharType* input; + const FilePath::CharType* expected; +}; + +struct UnaryBooleanTestData { + const FilePath::CharType* input; + bool expected; +}; + +struct BinaryTestData { + const FilePath::CharType* inputs[2]; + const FilePath::CharType* expected; +}; + +TEST(FilePathTest, DirName) { + const struct UnaryTestData cases[] = { + { FPL(""), FPL(".") }, + { FPL("aa"), FPL(".") }, + { FPL("/aa/bb"), FPL("/aa") }, + { FPL("/aa/bb/"), FPL("/aa") }, + { FPL("/aa/bb//"), FPL("/aa") }, + { FPL("/aa/bb/ccc"), FPL("/aa/bb") }, + { FPL("/aa"), FPL("/") }, + { FPL("/aa/"), FPL("/") }, + { FPL("/"), FPL("/") }, + { FPL("//"), FPL("//") }, + { FPL("///"), FPL("/") }, + { FPL("aa/"), FPL(".") }, + { FPL("aa/bb"), FPL("aa") }, + { FPL("aa/bb/"), FPL("aa") }, + { FPL("aa/bb//"), FPL("aa") }, + { FPL("aa//bb//"), FPL("aa") }, + { FPL("aa//bb/"), FPL("aa") }, + { FPL("aa//bb"), FPL("aa") }, + { FPL("//aa/bb"), FPL("//aa") }, + { FPL("//aa/"), FPL("//") }, + { FPL("//aa"), FPL("//") }, + { FPL("0:"), FPL(".") }, + { FPL("@:"), FPL(".") }, + { FPL("[:"), FPL(".") }, + { FPL("`:"), FPL(".") }, + { FPL("{:"), FPL(".") }, + { FPL("\xB3:"), FPL(".") }, + { FPL("\xC5:"), FPL(".") }, +#if defined(OS_WIN) + { FPL("\x0143:"), FPL(".") }, +#endif // OS_WIN +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { FPL("c:"), FPL("c:") }, + { FPL("C:"), FPL("C:") }, + { FPL("A:"), FPL("A:") }, + { FPL("Z:"), FPL("Z:") }, + { FPL("a:"), FPL("a:") }, + { FPL("z:"), FPL("z:") }, + { FPL("c:aa"), FPL("c:") }, + { FPL("c:/"), FPL("c:/") }, + { FPL("c://"), FPL("c://") }, + { FPL("c:///"), FPL("c:/") }, + { FPL("c:/aa"), FPL("c:/") }, + { FPL("c:/aa/"), FPL("c:/") }, + { FPL("c:/aa/bb"), FPL("c:/aa") }, + { FPL("c:aa/bb"), FPL("c:aa") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#if defined(FILE_PATH_USES_WIN_SEPARATORS) + { FPL("\\aa\\bb"), FPL("\\aa") }, + { FPL("\\aa\\bb\\"), FPL("\\aa") }, + { FPL("\\aa\\bb\\\\"), FPL("\\aa") }, + { FPL("\\aa\\bb\\ccc"), FPL("\\aa\\bb") }, + { FPL("\\aa"), FPL("\\") }, + { FPL("\\aa\\"), FPL("\\") }, + { FPL("\\"), FPL("\\") }, + { FPL("\\\\"), FPL("\\\\") }, + { FPL("\\\\\\"), FPL("\\") }, + { FPL("aa\\"), FPL(".") }, + { FPL("aa\\bb"), FPL("aa") }, + { FPL("aa\\bb\\"), FPL("aa") }, + { FPL("aa\\bb\\\\"), FPL("aa") }, + { FPL("aa\\\\bb\\\\"), FPL("aa") }, + { FPL("aa\\\\bb\\"), FPL("aa") }, + { FPL("aa\\\\bb"), FPL("aa") }, + { FPL("\\\\aa\\bb"), FPL("\\\\aa") }, + { FPL("\\\\aa\\"), FPL("\\\\") }, + { FPL("\\\\aa"), FPL("\\\\") }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { FPL("c:\\"), FPL("c:\\") }, + { FPL("c:\\\\"), FPL("c:\\\\") }, + { FPL("c:\\\\\\"), FPL("c:\\") }, + { FPL("c:\\aa"), FPL("c:\\") }, + { FPL("c:\\aa\\"), FPL("c:\\") }, + { FPL("c:\\aa\\bb"), FPL("c:\\aa") }, + { FPL("c:aa\\bb"), FPL("c:aa") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#endif // FILE_PATH_USES_WIN_SEPARATORS + }; + + for (size_t i = 0; i < arraysize(cases); ++i) { + FilePath input(cases[i].input); + FilePath observed = input.DirName(); + EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) << + "i: " << i << ", input: " << input.value(); + } +} + +TEST(FilePathTest, BaseName) { + const struct UnaryTestData cases[] = { + { FPL(""), FPL("") }, + { FPL("aa"), FPL("aa") }, + { FPL("/aa/bb"), FPL("bb") }, + { FPL("/aa/bb/"), FPL("bb") }, + { FPL("/aa/bb//"), FPL("bb") }, + { FPL("/aa/bb/ccc"), FPL("ccc") }, + { FPL("/aa"), FPL("aa") }, + { FPL("/"), FPL("/") }, + { FPL("//"), FPL("//") }, + { FPL("///"), FPL("/") }, + { FPL("aa/"), FPL("aa") }, + { FPL("aa/bb"), FPL("bb") }, + { FPL("aa/bb/"), FPL("bb") }, + { FPL("aa/bb//"), FPL("bb") }, + { FPL("aa//bb//"), FPL("bb") }, + { FPL("aa//bb/"), FPL("bb") }, + { FPL("aa//bb"), FPL("bb") }, + { FPL("//aa/bb"), FPL("bb") }, + { FPL("//aa/"), FPL("aa") }, + { FPL("//aa"), FPL("aa") }, + { FPL("0:"), FPL("0:") }, + { FPL("@:"), FPL("@:") }, + { FPL("[:"), FPL("[:") }, + { FPL("`:"), FPL("`:") }, + { FPL("{:"), FPL("{:") }, + { FPL("\xB3:"), FPL("\xB3:") }, + { FPL("\xC5:"), FPL("\xC5:") }, +#if defined(OS_WIN) + { FPL("\x0143:"), FPL("\x0143:") }, +#endif // OS_WIN +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { FPL("c:"), FPL("") }, + { FPL("C:"), FPL("") }, + { FPL("A:"), FPL("") }, + { FPL("Z:"), FPL("") }, + { FPL("a:"), FPL("") }, + { FPL("z:"), FPL("") }, + { FPL("c:aa"), FPL("aa") }, + { FPL("c:/"), FPL("/") }, + { FPL("c://"), FPL("//") }, + { FPL("c:///"), FPL("/") }, + { FPL("c:/aa"), FPL("aa") }, + { FPL("c:/aa/"), FPL("aa") }, + { FPL("c:/aa/bb"), FPL("bb") }, + { FPL("c:aa/bb"), FPL("bb") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#if defined(FILE_PATH_USES_WIN_SEPARATORS) + { FPL("\\aa\\bb"), FPL("bb") }, + { FPL("\\aa\\bb\\"), FPL("bb") }, + { FPL("\\aa\\bb\\\\"), FPL("bb") }, + { FPL("\\aa\\bb\\ccc"), FPL("ccc") }, + { FPL("\\aa"), FPL("aa") }, + { FPL("\\"), FPL("\\") }, + { FPL("\\\\"), FPL("\\\\") }, + { FPL("\\\\\\"), FPL("\\") }, + { FPL("aa\\"), FPL("aa") }, + { FPL("aa\\bb"), FPL("bb") }, + { FPL("aa\\bb\\"), FPL("bb") }, + { FPL("aa\\bb\\\\"), FPL("bb") }, + { FPL("aa\\\\bb\\\\"), FPL("bb") }, + { FPL("aa\\\\bb\\"), FPL("bb") }, + { FPL("aa\\\\bb"), FPL("bb") }, + { FPL("\\\\aa\\bb"), FPL("bb") }, + { FPL("\\\\aa\\"), FPL("aa") }, + { FPL("\\\\aa"), FPL("aa") }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { FPL("c:\\"), FPL("\\") }, + { FPL("c:\\\\"), FPL("\\\\") }, + { FPL("c:\\\\\\"), FPL("\\") }, + { FPL("c:\\aa"), FPL("aa") }, + { FPL("c:\\aa\\"), FPL("aa") }, + { FPL("c:\\aa\\bb"), FPL("bb") }, + { FPL("c:aa\\bb"), FPL("bb") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#endif // FILE_PATH_USES_WIN_SEPARATORS + }; + + for (size_t i = 0; i < arraysize(cases); ++i) { + FilePath input(cases[i].input); + FilePath observed = input.BaseName(); + EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) << + "i: " << i << ", input: " << input.value(); + } +} + +TEST(FilePathTest, Append) { + const struct BinaryTestData cases[] = { + { { FPL(""), FPL("cc") }, FPL("cc") }, + { { FPL("."), FPL("ff") }, FPL("ff") }, + { { FPL("/"), FPL("cc") }, FPL("/cc") }, + { { FPL("/aa"), FPL("") }, FPL("/aa") }, + { { FPL("/aa/"), FPL("") }, FPL("/aa") }, + { { FPL("//aa"), FPL("") }, FPL("//aa") }, + { { FPL("//aa/"), FPL("") }, FPL("//aa") }, + { { FPL("//"), FPL("aa") }, FPL("//aa") }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { { FPL("c:"), FPL("a") }, FPL("c:a") }, + { { FPL("c:"), FPL("") }, FPL("c:") }, + { { FPL("c:/"), FPL("a") }, FPL("c:/a") }, + { { FPL("c://"), FPL("a") }, FPL("c://a") }, + { { FPL("c:///"), FPL("a") }, FPL("c:/a") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#if defined(FILE_PATH_USES_WIN_SEPARATORS) + // Append introduces the default separator character, so these test cases + // need to be defined with different expected results on platforms that use + // different default separator characters. + { { FPL("\\"), FPL("cc") }, FPL("\\cc") }, + { { FPL("\\aa"), FPL("") }, FPL("\\aa") }, + { { FPL("\\aa\\"), FPL("") }, FPL("\\aa") }, + { { FPL("\\\\aa"), FPL("") }, FPL("\\\\aa") }, + { { FPL("\\\\aa\\"), FPL("") }, FPL("\\\\aa") }, + { { FPL("\\\\"), FPL("aa") }, FPL("\\\\aa") }, + { { FPL("/aa/bb"), FPL("cc") }, FPL("/aa/bb\\cc") }, + { { FPL("/aa/bb/"), FPL("cc") }, FPL("/aa/bb\\cc") }, + { { FPL("aa/bb/"), FPL("cc") }, FPL("aa/bb\\cc") }, + { { FPL("aa/bb"), FPL("cc") }, FPL("aa/bb\\cc") }, + { { FPL("a/b"), FPL("c") }, FPL("a/b\\c") }, + { { FPL("a/b/"), FPL("c") }, FPL("a/b\\c") }, + { { FPL("//aa"), FPL("bb") }, FPL("//aa\\bb") }, + { { FPL("//aa/"), FPL("bb") }, FPL("//aa\\bb") }, + { { FPL("\\aa\\bb"), FPL("cc") }, FPL("\\aa\\bb\\cc") }, + { { FPL("\\aa\\bb\\"), FPL("cc") }, FPL("\\aa\\bb\\cc") }, + { { FPL("aa\\bb\\"), FPL("cc") }, FPL("aa\\bb\\cc") }, + { { FPL("aa\\bb"), FPL("cc") }, FPL("aa\\bb\\cc") }, + { { FPL("a\\b"), FPL("c") }, FPL("a\\b\\c") }, + { { FPL("a\\b\\"), FPL("c") }, FPL("a\\b\\c") }, + { { FPL("\\\\aa"), FPL("bb") }, FPL("\\\\aa\\bb") }, + { { FPL("\\\\aa\\"), FPL("bb") }, FPL("\\\\aa\\bb") }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { { FPL("c:\\"), FPL("a") }, FPL("c:\\a") }, + { { FPL("c:\\\\"), FPL("a") }, FPL("c:\\\\a") }, + { { FPL("c:\\\\\\"), FPL("a") }, FPL("c:\\a") }, + { { FPL("c:\\"), FPL("") }, FPL("c:\\") }, + { { FPL("c:\\a"), FPL("b") }, FPL("c:\\a\\b") }, + { { FPL("c:\\a\\"), FPL("b") }, FPL("c:\\a\\b") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#else // FILE_PATH_USES_WIN_SEPARATORS + { { FPL("/aa/bb"), FPL("cc") }, FPL("/aa/bb/cc") }, + { { FPL("/aa/bb/"), FPL("cc") }, FPL("/aa/bb/cc") }, + { { FPL("aa/bb/"), FPL("cc") }, FPL("aa/bb/cc") }, + { { FPL("aa/bb"), FPL("cc") }, FPL("aa/bb/cc") }, + { { FPL("a/b"), FPL("c") }, FPL("a/b/c") }, + { { FPL("a/b/"), FPL("c") }, FPL("a/b/c") }, + { { FPL("//aa"), FPL("bb") }, FPL("//aa/bb") }, + { { FPL("//aa/"), FPL("bb") }, FPL("//aa/bb") }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { { FPL("c:/"), FPL("a") }, FPL("c:/a") }, + { { FPL("c:/"), FPL("") }, FPL("c:/") }, + { { FPL("c:/a"), FPL("b") }, FPL("c:/a/b") }, + { { FPL("c:/a/"), FPL("b") }, FPL("c:/a/b") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#endif // FILE_PATH_USES_WIN_SEPARATORS + }; + + for (size_t i = 0; i < arraysize(cases); ++i) { + FilePath root(cases[i].inputs[0]); + FilePath::StringType leaf(cases[i].inputs[1]); + FilePath observed = root.Append(leaf); + EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) << + "i: " << i << ", root: " << root.value() << ", leaf: " << leaf; + } +} + +TEST(FilePathTest, IsAbsolute) { + const struct UnaryBooleanTestData cases[] = { + { FPL(""), false }, + { FPL("a"), false }, + { FPL("c:"), false }, + { FPL("c:a"), false }, + { FPL("a/b"), false }, + { FPL("//"), true }, + { FPL("//a"), true }, + { FPL("c:a/b"), false }, + { FPL("?:/a"), false }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { FPL("/"), false }, + { FPL("/a"), false }, + { FPL("/."), false }, + { FPL("/.."), false }, + { FPL("c:/"), true }, + { FPL("c:/a"), true }, + { FPL("c:/."), true }, + { FPL("c:/.."), true }, + { FPL("C:/a"), true }, + { FPL("d:/a"), true }, +#else // FILE_PATH_USES_DRIVE_LETTERS + { FPL("/"), true }, + { FPL("/a"), true }, + { FPL("/."), true }, + { FPL("/.."), true }, + { FPL("c:/"), false }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#if defined(FILE_PATH_USES_WIN_SEPARATORS) + { FPL("a\\b"), false }, + { FPL("\\\\"), true }, + { FPL("\\\\a"), true }, + { FPL("a\\b"), false }, + { FPL("\\\\"), true }, + { FPL("//a"), true }, + { FPL("c:a\\b"), false }, + { FPL("?:\\a"), false }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { FPL("\\"), false }, + { FPL("\\a"), false }, + { FPL("\\."), false }, + { FPL("\\.."), false }, + { FPL("c:\\"), true }, + { FPL("c:\\"), true }, + { FPL("c:\\a"), true }, + { FPL("c:\\."), true }, + { FPL("c:\\.."), true }, + { FPL("C:\\a"), true }, + { FPL("d:\\a"), true }, +#else // FILE_PATH_USES_DRIVE_LETTERS + { FPL("\\"), true }, + { FPL("\\a"), true }, + { FPL("\\."), true }, + { FPL("\\.."), true }, + { FPL("c:\\"), false }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#endif // FILE_PATH_USES_WIN_SEPARATORS + }; + + for (size_t i = 0; i < arraysize(cases); ++i) { + FilePath input(cases[i].input); + bool observed = input.IsAbsolute(); + EXPECT_EQ(cases[i].expected, observed) << + "i: " << i << ", input: " << input.value(); + } +} |