COMMON: Fix path split+join combinations, add tests for same

This resolves multiple scenarios where a path ends up with a trailing
separator.
This commit is contained in:
Matthew Duggan 2022-12-31 17:29:42 +09:00
parent 6da117bc6d
commit c53b42311d
3 changed files with 85 additions and 10 deletions

View File

@ -61,13 +61,17 @@ String Path::toString(char separator) const {
return res;
}
size_t Path::findLastSeparator() const {
size_t Path::findLastSeparator(size_t last) const {
if (_str.size() < 2)
return String::npos;
size_t res = String::npos;
for (uint i = 0; i + 2 < _str.size(); i++) {
if (_str[i] == ESCAPER) {
i++;
if (_str[i] == ESCAPE_SEPARATOR)
res = i - 1;
if (last == String::npos || last > _str.size())
last = _str.size();
for (uint i = 0; i < last - 1; i++) {
if (_str[i] == ESCAPER && _str[i + 1] == ESCAPE_SEPARATOR) {
res = i;
}
}
@ -77,7 +81,8 @@ size_t Path::findLastSeparator() const {
Path Path::getParent() const {
if (_str.size() < 2)
return Path();
size_t separatorPos = findLastSeparator();
// ignore potential trailing separator
size_t separatorPos = findLastSeparator(_str.size() - 1);
if (separatorPos == String::npos)
return Path();
Path ret;
@ -88,7 +93,8 @@ Path Path::getParent() const {
Path Path::getLastComponent() const {
if (_str.size() < 2)
return *this;
size_t separatorPos = findLastSeparator();
// ignore potential trailing separator
size_t separatorPos = findLastSeparator(_str.size() - 1);
if (separatorPos == String::npos)
return *this;
Path ret;
@ -197,7 +203,7 @@ Path &Path::joinInPlace(const Path &x) {
return *this;
size_t lastSep = findLastSeparator();
if (!_str.empty() && (lastSep == String::npos || lastSep != _str.size() - 2) && x._str.hasPrefix(DIR_SEPARATOR))
if (!_str.empty() && (lastSep == String::npos || lastSep != _str.size() - 2) && !x._str.hasPrefix(DIR_SEPARATOR))
_str += DIR_SEPARATOR;
_str += x._str;

View File

@ -49,7 +49,7 @@ private:
String _str;
String getIdentifierString() const;
size_t findLastSeparator() const;
size_t findLastSeparator(size_t last = String::npos) const;
public:
/**

69
test/common/path.h Normal file
View File

@ -0,0 +1,69 @@
#include <cxxtest/TestSuite.h>
#include "common/path.h"
static const char *TEST_PATH = "parent/dir/file.txt";
class PathTestSuite : public CxxTest::TestSuite
{
public:
void test_Path() {
Common::Path p;
TS_ASSERT_EQUALS(p.toString(), Common::String());
Common::Path p2(TEST_PATH);
TS_ASSERT_EQUALS(p2.toString(), Common::String(TEST_PATH));
}
void test_getLastComponent() {
Common::Path p(TEST_PATH);
TS_ASSERT_EQUALS(p.getLastComponent().toString(), "file.txt");
}
void test_getParent() {
Common::Path p(TEST_PATH);
TS_ASSERT_EQUALS(p.getParent().toString(), "parent/dir/");
// TODO: should this work?
TS_ASSERT_EQUALS(p.getParent().getLastComponent().toString(), "dir/");
}
void test_join() {
Common::Path p("dir");
Common::Path p2 = p.join("file.txt");
TS_ASSERT_EQUALS(p2.toString(), "dir/file.txt");
Common::Path p3(TEST_PATH);
Common::Path p4 = p3.getParent().join("other.txt");
TS_ASSERT_EQUALS(p4.toString(), "parent/dir/other.txt");
}
// Ensure we can joinInPlace correctly with leading or trailing separators
void test_joinInPlace() {
Common::Path p("abc/def");
p.joinInPlace("file.txt");
TS_ASSERT_EQUALS(p.toString(), "abc/def/file.txt");
Common::Path p2("xyz/def");
p2.joinInPlace(Common::Path("file.txt"));
TS_ASSERT_EQUALS(p2.toString(), "xyz/def/file.txt");
Common::Path p3("ghi/def/");
p3.joinInPlace(Common::Path("file.txt"));
TS_ASSERT_EQUALS(p3.toString(), "ghi/def/file.txt");
Common::Path p4("123/def");
p4.joinInPlace(Common::Path("/file4.txt"));
TS_ASSERT_EQUALS(p4.toString(), "123/def/file4.txt");
}
void test_separator() {
Common::Path p(TEST_PATH, '\\');
TS_ASSERT_EQUALS(p.getLastComponent().toString(), TEST_PATH);
TS_ASSERT_EQUALS(p.getParent().toString(), "");
Common::Path p2(TEST_PATH, 'e');
TS_ASSERT_EQUALS(p2.getLastComponent().toString(), ".txt");
TS_ASSERT_EQUALS(p2.getParent().toString('#'), "par#nt/dir/fil#");
TS_ASSERT_EQUALS(p2.getParent().getParent().toString('#'), "par#");
}
};