From e7ad60c09541176401a83da8220d517dfffa407e Mon Sep 17 00:00:00 2001 From: Tomasz Iwanek Date: Thu, 4 Apr 2013 08:58:01 +0200 Subject: [PATCH] DPL Path fix [Issue#] LINUXWRT-149 [Feature] new method for Path class [Cause] N/A [Solution] N/A [Verification] Run tests: wrt-commons-tests-utils --output=text --regexp='path' Change-Id: I1f65a1d3f324b5b8ce0d217e511f320337e0866b --- modules/utils/include/dpl/utils/path.h | 67 +++++++- modules/utils/src/path.cpp | 137 ++++++++++++++-- tests/utils/path_tests.cpp | 289 +++++++++++++++++++++++++++++++-- 3 files changed, 458 insertions(+), 35 deletions(-) diff --git a/modules/utils/include/dpl/utils/path.h b/modules/utils/include/dpl/utils/path.h index 6e025db..de8840b 100644 --- a/modules/utils/include/dpl/utils/path.h +++ b/modules/utils/include/dpl/utils/path.h @@ -48,17 +48,29 @@ namespace Utils { * * Class for expressing paths not limited not existing ones. * It's possible to check if path exists, remove it or iterate it if it's directory + * + * Created Path object allways contains absolute path, never relative path. + * Simplifies common usage cases: + * - path construction (with /= and / operators) + * - directory iterator (begin(), end(), iterator construction) + * - receiving filenames and directory names of given paths + * - checking what is pointed by path (Exists(), IsFile(), IsDir()) + * + * Check tests for details of usage. */ class Path { public: DECLARE_EXCEPTION_TYPE(DPL::Exception, BaseException) - DECLARE_EXCEPTION_TYPE(BaseException, AlreadyExists) - DECLARE_EXCEPTION_TYPE(BaseException, NotExists) - DECLARE_EXCEPTION_TYPE(BaseException, NotDirectory) - DECLARE_EXCEPTION_TYPE(BaseException, OperationFailed) - DECLARE_EXCEPTION_TYPE(BaseException, EmptyPath) - DECLARE_EXCEPTION_TYPE(BaseException, InternalError) + DECLARE_EXCEPTION_TYPE(BaseException, AlreadyExists) //path already exists + DECLARE_EXCEPTION_TYPE(BaseException, NotPrefix) //given path is not prefix of this path + DECLARE_EXCEPTION_TYPE(BaseException, NotExists) //file not exists + DECLARE_EXCEPTION_TYPE(BaseException, NotDirectory) //directory nto exists + DECLARE_EXCEPTION_TYPE(BaseException, OperationFailed) //operation failed due to system error(permission etc..) + DECLARE_EXCEPTION_TYPE(BaseException, EmptyPath) //object cannot be constructed with empty path + DECLARE_EXCEPTION_TYPE(BaseException, InternalError) //internal error / wrong path usage + DECLARE_EXCEPTION_TYPE(BaseException, CannotCopy) //cannot make copy + DECLARE_EXCEPTION_TYPE(BaseException, RootDirectoryError) //operation cannot be done with root diretory class Iterator : public std::iterator { @@ -82,8 +94,14 @@ public: explicit Path(const DPL::String & str); explicit Path(const std::string & str); explicit Path(const char * str); + Path(); /** + * @brief DirectoryPath shell's dirname equivalent as path + * @return directory path + */ + Path DirectoryPath() const; + /** * @brief DirectoryName shell's dirname equivalent * @return directory name of given path */ @@ -92,7 +110,7 @@ public: * @brief Basename shell's basename equivalent * @return base name of given path */ - std::string Basename() const; + std::string Filename() const; /** * @brief Fullpath fullpath based on current working diretory * @return full path @@ -103,6 +121,15 @@ public: bool IsDir() const; bool IsFile() const; bool IsSymlink() const; + std::size_t Size() const; + /** + * @brief isSubPath Returns relative path to given base + * @param prefix base path + * @return reltive path + * + * @throws If prefix does not match to this path object + */ + bool isSubPath(const Path & other) const; bool operator==(const Path & other) const; bool operator!=(const Path & other) const; @@ -120,8 +147,10 @@ public: Iterator begin() const; Iterator end() const; + //root error - throws error on root directory + void RootGuard() const; + private: - Path(); void Append(const std::string& part); void Construct(const std::string & src); @@ -151,6 +180,12 @@ void MakeEmptyFile(const Path & path); void Remove(const Path & path); /** + * @brief TryRemove tries to remvoe path + * @param path returns status of removal + */ +bool TryRemove(const Path & path); + +/** * @brief Rename renames(moves) current path * * If you uses this method string to path is internally change @@ -167,6 +202,22 @@ void Rename(const Path & from, const Path & to); */ bool Exists(const Path & path); +/** + * @brief Copy file + * + * @param from source path + * @param to target path + */ +void CopyFile(const Path & from, const Path & to); + +/** + * @brief Copy directory recursively + * + * @param from source directory path + * @param to target directory path + */ +void CopyDir(const Path & from, const Path & to); + } } diff --git a/modules/utils/src/path.cpp b/modules/utils/src/path.cpp index d2867d4..5f36011 100644 --- a/modules/utils/src/path.cpp +++ b/modules/utils/src/path.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include @@ -138,7 +140,7 @@ void Path::Construct(const std::string & src) Tokenize(src, "\\/", std::inserter(m_parts, m_parts.end()), true); } -Path::Path() //for private usage +Path::Path() { } @@ -149,7 +151,7 @@ std::string Path::DirectoryName() const return std::string("/") + ret; } -std::string Path::Basename() const +std::string Path::Filename() const { if(m_parts.empty()) return ""; else return m_parts.back(); @@ -179,16 +181,22 @@ Path::Iterator Path::end() const return Iterator(); } +void Path::RootGuard() const +{ + if(m_parts.empty()) Throw(RootDirectoryError); +} bool Path::Exists() const { struct stat tmp; + memset(&tmp, 0, sizeof(struct stat)); return (0 == lstat(Fullpath().c_str(), &tmp)); } bool Path::IsDir() const { struct stat tmp; + memset(&tmp, 0, sizeof(struct stat)); if (-1 == lstat(Fullpath().c_str(), &tmp)) { ThrowMsg(NotExists, DPL::GetErrnoString()); @@ -199,6 +207,7 @@ bool Path::IsDir() const bool Path::IsFile() const { struct stat tmp; + memset(&tmp, 0, sizeof(struct stat)); if (-1 == lstat(Fullpath().c_str(), &tmp)) { ThrowMsg(NotExists, DPL::GetErrnoString()); @@ -209,6 +218,7 @@ bool Path::IsFile() const bool Path::IsSymlink() const { struct stat tmp; + memset(&tmp, 0, sizeof(struct stat)); if (-1 == lstat(Fullpath().c_str(), &tmp)) { ThrowMsg(NotExists, DPL::GetErrnoString()); @@ -228,14 +238,14 @@ bool Path::operator!=(const Path & other) const Path Path::operator/(const DPL::String& part) const { - Path newOne; + Path newOne(*this); newOne.Append(ToUTF8String(part)); return newOne; } Path Path::operator/(const std::string& part) const { - Path newOne; + Path newOne(*this); newOne.Append(part); return newOne; } @@ -272,18 +282,53 @@ void Path::Append(const std::string& part) std::copy(tokens.begin(), tokens.end(), std::inserter(m_parts, m_parts.end())); } +Path Path::DirectoryPath() const +{ + Path npath; + if(m_parts.empty()) ThrowMsg(InternalError, "Asking DirectoryPath for root directory"); + std::copy(m_parts.begin(), --m_parts.end(), std::back_inserter(npath.m_parts)); + return npath; +} + +std::size_t Path::Size() const +{ + struct stat tmp; + memset(&tmp, 0, sizeof(struct stat)); + if (-1 == lstat(Fullpath().c_str(), &tmp)) + { + ThrowMsg(NotExists, DPL::GetErrnoString()); + } + return tmp.st_size; +} + +bool Path::isSubPath(const Path & other) const +{ + typedef std::vector::const_iterator Iter; + Iter otherIter = other.m_parts.begin(); + for(Iter iter = m_parts.begin(); iter != m_parts.end(); iter++) + { + if(otherIter == other.m_parts.end()) return false; + if(*iter != *otherIter) return false; + otherIter++; + } + return true; +} + void MakeDir(const Path & path, mode_t mode) { + path.RootGuard(); if(!WrtUtilMakeDir(path.Fullpath(), mode)) ThrowMsg(Path::OperationFailed, "Cannot make directory"); } void MakeEmptyFile(const Path & path) { + path.RootGuard(); std::string fp = path.Fullpath(); FILE* fd = fopen(fp.c_str(), "wx"); if(!fd) { struct stat st; + memset(&st, 0, sizeof(struct stat)); if(lstat(fp.c_str(), &st) == 0) { ThrowMsg(Path::AlreadyExists, "File already exists"); @@ -298,33 +343,42 @@ void MakeEmptyFile(const Path & path) void Remove(const Path & path) { + path.RootGuard(); if(!WrtUtilRemove(path.Fullpath())) ThrowMsg(Path::OperationFailed, "Cannot remove path"); } +bool TryRemove(const Path & path) +{ + path.RootGuard(); + if(!WrtUtilRemove(path.Fullpath())) return false; + return true; +} + void Rename(const Path & from, const Path & to) { + from.RootGuard(); + to.RootGuard(); if(from == to) { return; } - int code = 0; - if( (code = rename(from.Fullpath().c_str(), to.Fullpath().c_str())) ) + if(0 != rename(from.Fullpath().c_str(), to.Fullpath().c_str())) { - if(code == EXDEV) + if(errno == EXDEV) { - Try + if(from.IsDir()) { - DPL::FileInput in(from.Fullpath()); - DPL::FileOutput out(to.Fullpath()); - DPL::Copy(&in, &out); + CopyDir(from, to); + Remove(from); } - Catch(DPL::FileInput::Exception::Base) + else if(from.IsFile() || from.IsSymlink()) { - ThrowMsg(Path::OperationFailed, "Cannot open input file " << from.Fullpath()); + CopyFile(from, to); + Remove(from); } - Catch(DPL::FileOutput::Exception::Base) + else { - ThrowMsg(Path::OperationFailed, "Cannot open output file " << to.Fullpath()); + ThrowMsg(Path::OperationFailed, DPL::GetErrnoString()); } } else @@ -334,6 +388,59 @@ void Rename(const Path & from, const Path & to) } } +void CopyFile(const Path & from, const Path & to) +{ + from.RootGuard(); + to.RootGuard(); + Try + { + DPL::FileInput input(from.Fullpath()); + DPL::FileOutput output(to.Fullpath()); + DPL::Copy(&input, &output); + } + Catch(DPL::FileInput::Exception::Base) + { + LogError("File input error"); + ReThrowMsg(DPL::CopyFailed, std::string("File input error") + from.Fullpath()); + } + Catch(DPL::FileOutput::Exception::Base) + { + LogError("File output error"); + ReThrowMsg(DPL::CopyFailed, std::string("File output error") + to.Fullpath()); + } + Catch(DPL::CopyFailed) + { + LogError("File copy error"); + ReThrowMsg(DPL::CopyFailed, std::string("File copy error") + from.Fullpath()); + } +} + +void CopyDir(const Path & from, const Path & to) +{ + from.RootGuard(); + to.RootGuard(); + if(from.isSubPath(to)) + { + ThrowMsg(Path::CannotCopy, "Cannot copy content of directory to it's sub directory"); + } + MakeDir(to); + FOREACH(item, from) + { + if(item->IsDir()) + { + CopyDir(*item, to / item->Filename()); + } + else if(item->IsFile() || item->IsSymlink()) + { + CopyFile(*item, to / item->Filename()); + } + else + { + Throw(Path::OperationFailed); + } + } +} + bool Exists(const Path & path) { return path.Exists(); diff --git a/tests/utils/path_tests.cpp b/tests/utils/path_tests.cpp index 1a86a21..4e957e2 100644 --- a/tests/utils/path_tests.cpp +++ b/tests/utils/path_tests.cpp @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include #include #include @@ -39,6 +42,17 @@ namespace { std::string rootTest = "/tmp/wrttest/"; } +#define ROOTGUARD_TESTMETHOD(FUNC) \ +{ \ + bool catched = false; \ + Try { \ + FUNC; \ + } Catch(Path::RootDirectoryError) { \ + catched = true; \ + } \ + RUNNER_ASSERT_MSG(catched, "Use of method should be protected against root diretory"); \ +} \ + RUNNER_TEST_GROUP_INIT(DPL_Path) /* @@ -51,6 +65,7 @@ RUNNER_TEST(path_mkfile) DPL::ScopedDir sd(rootTest); struct stat st; + memset(&st, 0, sizeof(struct stat)); Path path = Path(rootTest) / "touch.txt"; RUNNER_ASSERT_MSG(lstat(path.Fullpath().c_str(), &st) != 0, "File should not be created"); RUNNER_ASSERT(!path.Exists()); @@ -118,6 +133,7 @@ RUNNER_TEST(path_mkdir) DPL::ScopedDir sd(rootTest); struct stat st; + memset(&st, 0, sizeof(struct stat)); Path path = Path(rootTest) / "touchDir"; RUNNER_ASSERT_MSG(lstat(path.Fullpath().c_str(), &st) != 0, "Directory should not be created"); RUNNER_ASSERT(!path.Exists()); @@ -139,6 +155,7 @@ RUNNER_TEST(path_symlink) DPL::ScopedDir sd(rootTest); struct stat st; + memset(&st, 0, sizeof(struct stat)); Path path = Path(rootTest) / "symlink"; RUNNER_ASSERT_MSG(lstat(path.Fullpath().c_str(), &st) != 0, "Symlink should not be created"); RUNNER_ASSERT(!path.Exists()); @@ -178,7 +195,7 @@ RUNNER_TEST(path_construction_root) { Path path1(std::string("/")); RUNNER_ASSERT(path1.Fullpath() == "/"); - RUNNER_ASSERT(path1.Basename() == ""); + RUNNER_ASSERT(path1.Filename() == ""); bool passed = false; Try { @@ -202,7 +219,7 @@ RUNNER_TEST(path_construction_1) Path path1(std::string("/test/bin/file")); RUNNER_ASSERT(path1.Fullpath() == "/test/bin/file"); - RUNNER_ASSERT(path1.Basename() == "file"); + RUNNER_ASSERT(path1.Filename() == "file"); RUNNER_ASSERT(path1.DirectoryName() == "/test/bin"); } @@ -218,7 +235,7 @@ RUNNER_TEST(path_construction_2) Path path2(std::string("test/bin/file.eas")); RUNNER_ASSERT(path2.Fullpath() == cwd + "/test/bin/file.eas"); - RUNNER_ASSERT(path2.Basename() == "file.eas"); + RUNNER_ASSERT(path2.Filename() == "file.eas"); RUNNER_ASSERT(path2.DirectoryName() == cwd + "/test/bin"); } @@ -234,7 +251,7 @@ RUNNER_TEST(path_construction_3) Path path3("test/23/abc"); RUNNER_ASSERT(path3.Fullpath() == cwd + "/test/23/abc"); - RUNNER_ASSERT(path3.Basename() == "abc"); + RUNNER_ASSERT(path3.Filename() == "abc"); RUNNER_ASSERT(path3.DirectoryName() == cwd + "/test/23"); } @@ -249,7 +266,7 @@ RUNNER_TEST(path_construction_4) Path path4("/test/bin/abc"); RUNNER_ASSERT(path4.Fullpath() == "/test/bin/abc"); - RUNNER_ASSERT(path4.Basename() == "abc"); + RUNNER_ASSERT(path4.Filename() == "abc"); RUNNER_ASSERT(path4.DirectoryName() == "/test/bin"); } @@ -265,7 +282,7 @@ RUNNER_TEST(path_construction_5) Path path5(DPL::String(L"test/bin/file.st.exe")); RUNNER_ASSERT(path5.Fullpath() == cwd + "/test/bin/file.st.exe"); - RUNNER_ASSERT(path5.Basename() == "file.st.exe"); + RUNNER_ASSERT(path5.Filename() == "file.st.exe"); RUNNER_ASSERT(path5.DirectoryName() == cwd + "/test/bin"); } @@ -280,7 +297,7 @@ RUNNER_TEST(path_construction_6) Path path6(DPL::String(L"/test/bin/file")); RUNNER_ASSERT(path6.Fullpath() == "/test/bin/file"); - RUNNER_ASSERT(path6.Basename() == "file"); + RUNNER_ASSERT(path6.Filename() == "file"); RUNNER_ASSERT(path6.DirectoryName() == "/test/bin"); } @@ -296,7 +313,7 @@ RUNNER_TEST(path_construction_7) Path path7 = Path("test") / "a///23/lol"; RUNNER_ASSERT(path7.Fullpath() == cwd + "/test/a/23/lol"); - RUNNER_ASSERT(path7.Basename() == "lol"); + RUNNER_ASSERT(path7.Filename() == "lol"); RUNNER_ASSERT(path7.DirectoryName() == cwd + "/test/a/23"); } @@ -311,7 +328,7 @@ RUNNER_TEST(path_construction_8) Path path8 = Path("/test/bin/") / "123" / "dir1.dll"; RUNNER_ASSERT(path8.Fullpath() == "/test/bin/123/dir1.dll"); - RUNNER_ASSERT(path8.Basename() == "dir1.dll"); + RUNNER_ASSERT(path8.Filename() == "dir1.dll"); RUNNER_ASSERT(path8.DirectoryName() == "/test/bin/123"); } @@ -326,7 +343,7 @@ RUNNER_TEST(path_construction_9) Path path9 = Path("/test/bin/file.txt//"); RUNNER_ASSERT(path9.Fullpath() == "/test/bin/file.txt"); - RUNNER_ASSERT(path9.Basename() == "file.txt"); + RUNNER_ASSERT(path9.Filename() == "file.txt"); RUNNER_ASSERT(path9.DirectoryName() == "/test/bin"); } @@ -342,7 +359,7 @@ RUNNER_TEST(path_construction_10) path10 /= std::string("two"); path10 /= DPL::String(L"three"); RUNNER_ASSERT(path10.Fullpath() == "/test/one/two/three"); - RUNNER_ASSERT(path10.Basename() == "three"); + RUNNER_ASSERT(path10.Filename() == "three"); RUNNER_ASSERT(path10.DirectoryName() == "/test/one/two"); } @@ -356,6 +373,7 @@ RUNNER_TEST(path_remove_valid) DPL::ScopedDir sd(rootTest); struct stat st; + memset(&st, 0, sizeof(struct stat)); Path path = Path(rootTest) / "touchDir"; RUNNER_ASSERT(!path.Exists()); @@ -377,6 +395,39 @@ RUNNER_TEST(path_remove_valid) } /* +Name: path_try_remove +Description: tests removing paths +Expected: successfull path remove once +*/ +RUNNER_TEST(path_try_remove_valid) +{ + DPL::ScopedDir sd(rootTest); + + struct stat st; + memset(&st, 0, sizeof(struct stat)); + Path path = Path(rootTest) / "touchDir"; + RUNNER_ASSERT(!path.Exists()); + + MakeDir(path); + RUNNER_ASSERT_MSG(lstat(path.Fullpath().c_str(), &st) == 0, "Directory should be created"); + RUNNER_ASSERT(path.Exists()); + + RUNNER_ASSERT(TryRemove(path)); + RUNNER_ASSERT(!TryRemove(path)); + RUNNER_ASSERT_MSG(lstat(path.Fullpath().c_str(), &st) != 0, "Directory should not be created"); + RUNNER_ASSERT(!path.Exists()); + + MakeEmptyFile(path); + RUNNER_ASSERT_MSG(lstat(path.Fullpath().c_str(), &st) == 0, "File should be created"); + RUNNER_ASSERT(path.Exists()); + + RUNNER_ASSERT(TryRemove(path)); + RUNNER_ASSERT(!TryRemove(path)); + RUNNER_ASSERT_MSG(lstat(path.Fullpath().c_str(), &st) != 0, "File should not be created"); + RUNNER_ASSERT(!path.Exists()); +} + +/* Name: path_remove_invalid Description: tests removing invalid paths Expected: failure at path remove @@ -409,6 +460,7 @@ RUNNER_TEST(path_rename) DPL::ScopedDir sd(rootTest); struct stat st; + memset(&st, 0, sizeof(struct stat)); Path path = Path(rootTest) / "touchDir"; Path dirpath = Path(rootTest) / "directory"; Path path2 = dirpath / "touchDir2"; @@ -427,8 +479,220 @@ RUNNER_TEST(path_rename) Rename(path2, path); RUNNER_ASSERT(path.Exists()); RUNNER_ASSERT(!path2.Exists()); +} + +/* +Name: path_rename_xdev +Description: tests path renaming between devices +Expected: path is successfully renamed +*/ +RUNNER_TEST(path_rename_xdev) +{ + //assuming /opt/usr and /usr is normally on other partitions on device + //TODO: better + Path path = Path("/opt/usr") / "touchDir"; + Path dirpath = path / "directory"; + Path filepath = path / "file.txt"; + + Path path2 = Path("/usr") / "touchDir2"; + Path dirpath2 = path2 / "directory"; + Path filepath2 = path2 / "file.txt"; + + TryRemove(path); + + MakeDir(path); + MakeDir(dirpath); + MakeEmptyFile(filepath); + + RUNNER_ASSERT(path.Exists()); + RUNNER_ASSERT(dirpath.Exists()); + RUNNER_ASSERT(filepath.Exists()); + RUNNER_ASSERT(!path2.Exists()); + RUNNER_ASSERT(!dirpath2.Exists()); + RUNNER_ASSERT(!filepath2.Exists()); + + Rename(path, path2); + RUNNER_ASSERT(!path.Exists()); + RUNNER_ASSERT(!dirpath.Exists()); + RUNNER_ASSERT(!filepath.Exists()); + RUNNER_ASSERT(path2.Exists()); + RUNNER_ASSERT(dirpath2.Exists()); + RUNNER_ASSERT(filepath2.Exists()); + + Rename(path2, path); + RUNNER_ASSERT(path.Exists()); + RUNNER_ASSERT(dirpath.Exists()); + RUNNER_ASSERT(filepath.Exists()); + RUNNER_ASSERT(!path2.Exists()); + RUNNER_ASSERT(!dirpath2.Exists()); + RUNNER_ASSERT(!filepath2.Exists()); + + Remove(path); +} - //TODO: test for different devices +/** + * Name: path_basename + * Description: check basename equivalents + * Expected: failure + */ +RUNNER_TEST(path_basename) +{ + DPL::ScopedDir sd(rootTest); + Path path = Path(rootTest) / "directory" / "touch.txt"; + RUNNER_ASSERT(path.DirectoryName() == path.DirectoryPath().Fullpath()); + RUNNER_ASSERT(path.DirectoryPath().DirectoryName() == path.DirectoryPath().DirectoryPath().Fullpath()); +} + +/** + * Name: path_safe + * Description: check if operations cannot be executed on root directory + * + * This is check because of default path is root and it should not be used usually + * Default constructor is unfortnatelly easier to use + * + * Expected: failure + */ +RUNNER_TEST(path_safe) +{ + DPL::ScopedDir sd(rootTest); + Path normal = Path(rootTest) / "directory" / "touch.txt"; + Path root("/"); + ROOTGUARD_TESTMETHOD( Rename(normal, root) ); + ROOTGUARD_TESTMETHOD( Rename(root, root) ); + ROOTGUARD_TESTMETHOD( Rename(root, normal) ); + ROOTGUARD_TESTMETHOD( CopyDir(normal, root) ); + ROOTGUARD_TESTMETHOD( CopyDir(root, root) ); + ROOTGUARD_TESTMETHOD( CopyDir(root, normal) ); + ROOTGUARD_TESTMETHOD( CopyFile(normal, root) ); + ROOTGUARD_TESTMETHOD( CopyFile(root, root) ); + ROOTGUARD_TESTMETHOD( CopyFile(root, normal) ); + ROOTGUARD_TESTMETHOD( Remove(root) ); + ROOTGUARD_TESTMETHOD( MakeEmptyFile(root) ); + ROOTGUARD_TESTMETHOD( MakeDir(root) ); +} + +/** + * Name: path_size + * Description: check testing size of file + * Expected: correct size + */ +RUNNER_TEST(path_size) +{ + DPL::ScopedDir sd(rootTest); + Path path = Path(rootTest) / "touch.txt"; + DPL::Utils::MakeEmptyFile(path); + RUNNER_ASSERT(path.Size() == 0); + + { + DPL::FileOutput out(path.Fullpath()); + DPL::BinaryQueue bq; + bq.AppendCopy("123456789", 9); + out.Write(bq, bq.Size()); + out.Close(); + RUNNER_ASSERT(path.Size() == 9); + } + + { + DPL::FileOutput out(path.Fullpath()); + DPL::BinaryQueue bq; + bq.AppendCopy("123456789", 4); + out.Write(bq, bq.Size()); + out.Close(); + RUNNER_ASSERT(path.Size() == 4); + } +} + +/** +Name: path_copy +Description: tests path coping directory andfiles +Expected: coping should be done +*/ +RUNNER_TEST(path_copy_directory) +{ + DPL::ScopedDir sd(rootTest); + + Path path = Path(rootTest) / "sourceDir"; + Path innerPath = Path(rootTest) / "sourceDir" / "level1" ; + Path innerPath2 = Path(rootTest) / "sourceDir" / "level1" / "level2"; + Path file1 = Path(rootTest) / "sourceDir" / "level1" / "level2" / "file1.txt"; + Path file2 = Path(rootTest) / "sourceDir" / "level1" / "level2" / "file2.txt"; + Path file3 = Path(rootTest) / "sourceDir" / "level1" / "file3.txt"; + Path file4 = Path(rootTest) / "sourceDir" / "file4.txt"; + + Path tfile1 = Path(rootTest) / "targetDir" / "level1" / "level2" / "file1.txt"; + Path tfile2 = Path(rootTest) / "targetDir" / "level1" / "level2" / "file2.txt"; + Path tfile3 = Path(rootTest) / "targetDir" / "level1" / "file3.txt"; + Path tfile4 = Path(rootTest) / "targetDir" / "file4.txt"; + + Path target = Path(rootTest) / "targetDir"; + + DPL::Utils::MakeDir(path); + DPL::Utils::MakeDir(innerPath); + DPL::Utils::MakeDir(innerPath2); + DPL::Utils::MakeEmptyFile(file1); + DPL::Utils::MakeEmptyFile(file2); + DPL::Utils::MakeEmptyFile(file3); + DPL::Utils::MakeEmptyFile(file4); + + DPL::Utils::CopyDir(path, target); + + RUNNER_ASSERT_MSG(tfile1.Exists(), tfile1.Fullpath() + " not exists"); + RUNNER_ASSERT_MSG(tfile1.IsFile(), tfile1.Fullpath() + " is not file"); + RUNNER_ASSERT_MSG(tfile2.Exists(), tfile2.Fullpath() + " not exists"); + RUNNER_ASSERT_MSG(tfile2.IsFile(), tfile2.Fullpath() + " is not file"); + RUNNER_ASSERT_MSG(tfile3.Exists(), tfile3.Fullpath() + " not exists"); + RUNNER_ASSERT_MSG(tfile3.IsFile(), tfile3.Fullpath() + " is not file"); + RUNNER_ASSERT_MSG(tfile4.Exists(), tfile4.Fullpath() + " not exists"); + RUNNER_ASSERT_MSG(tfile4.IsFile(), tfile4.Fullpath() + " is not file"); +} + +/* +Name: path_copy_inner +Description: tests path coping to subdirectory +Expected: coping shoudl fail +*/ +RUNNER_TEST(path_copy_inner) +{ + DPL::ScopedDir sd(rootTest); + + Path path = Path(rootTest) / "touchDir"; + Path inner = Path(rootTest) / "touchDir" / "innerDirectory"; + + bool exceptionCatched = false; + Try + { + DPL::Utils::CopyDir(path, inner); + } + Catch(DPL::Utils::Path::CannotCopy) + { + exceptionCatched = true; + } + RUNNER_ASSERT_MSG(exceptionCatched, "Copy should fail"); +} + +/* +Name: issubpath +Description: tests method compare if one path is subpath of another +Expected: correct results +*/ +RUNNER_TEST(path_issubpath) +{ + Path path1 = Path(rootTest) / "touchDir/asd/sdf"; + Path path2 = Path(rootTest) / "touchDir/asd/sdf/123"; + Path path3 = Path(rootTest) / "touchDir/asd/sdno"; + Path path4 = Path("/"); + + RUNNER_ASSERT(path1.isSubPath(path2)); + RUNNER_ASSERT(!path1.isSubPath(path3)); + RUNNER_ASSERT(!path1.isSubPath(path4)); + + RUNNER_ASSERT(!path2.isSubPath(path1)); + RUNNER_ASSERT(!path2.isSubPath(path3)); + RUNNER_ASSERT(!path2.isSubPath(path4)); + + RUNNER_ASSERT(path4.isSubPath(path1)); + RUNNER_ASSERT(path4.isSubPath(path2)); + RUNNER_ASSERT(path4.isSubPath(path3)); } /* @@ -441,6 +705,7 @@ RUNNER_TEST(path_rename_same) DPL::ScopedDir sd(rootTest); struct stat st; + memset(&st, 0, sizeof(struct stat)); Path path = Path(rootTest) / "touchDir"; MakeDir(path); -- 2.7.4