aboutsummaryrefslogblamecommitdiffstats
path: root/util/folder/path_ut.cpp
blob: f06fec78ee6fe012c83c64b6527478ec2baf7d60 (plain) (tree)
1
2
3
4
5
6
7
8
9
                 
                      
                    
 
                                                  
 
                               
                                 
                                
                               
                             
 
                    


                       






                                                                              
                                            




                                   
                                                  


                                     
                                                         
                                                            

                                                                          









                                       
                                 







                                           
                                                   









                                       
                             
                     
                                                           



                      
                         
            

                                                                                     
 

                                                                                  
 

                                                                                         
     

                                                                                   
 

                                                                                 
      
     


                                                                                         
      
     
 
                          
                                    
                                                                             
 
                               
             
                                                                       
      
     
 
                               
                                         



                                                                                   
     
 
                             






                                        
 
                                  

















                                                                             
                                 
                                                                                            
     
 
             
                               
                                                           






                                                                      
                                                                      
                                                                      
                                                                                                  
     
      
 
                                         
                                                         
                                                                                    
                                   
            
                                                                                                
     
                                                                                            
      
     
 
                                            

                                                              
                                                                                                
     
                                                                                                    

      
                                                 

                                                                
                                                                                                  
     
                                                                                                      

      
                           
                                          
 




                                            
 
                                  
                                                    
 
                                  


                                
 
                                                     
     
 
             
                            







                                                     
                                                                    

      
                      
                                                                                     
     
 
                                
                                                             
 

                                                      
 


                                                        
 




                                                       
            

                                                            
      
     
 







































                                                                      
                                   




                                                                                                              
                                                                                           
     
 
                                
                                                             
 
                                                                   
            


                                                                               
     


                                                                             
      
                                                                
 

                                                                          
 

                                                               
 


                                                                     
 

                                                      
            
                                                                                         
     
                                                                                       
      
 




                                              
 
                                  
                                                                            


                                                                                
                                                                  
     
                                                                       


                                                                             
                                                                
      
     












                                                                               
             
                                         

                                       
                                                              
                                              
                                                                    
         
                                                   

                                
                                                                 


                                      

                                             
     
                                          

                                       
                                                              
                                              
                                                                    
         
                                                   

                                
                                                                 


                                       

                                             
     
                                                

                                       
                                                                    
                                                 
                                                              
                                              
                                                                    
         
                                                   

                                
                                                                                    


                                               


                                                 
     
                                                 

                                       
                                                                    
                                                 
                                                              
                                              
                                                                    
         
                                                   

                                
                                                                                    


                                                


                                                 
      

























































































































































































































































































                                                                                                    



















                                                                                           
 
#include "path.h"
#include "pathsplit.h"
#include "dirut.h"
#include "tempdir.h"

#include <library/cpp/testing/unittest/registar.h>

#include <util/generic/scope.h>
#include <util/system/platform.h>
#include <util/system/yassert.h>
#include <util/stream/output.h>
#include <util/stream/file.h>
#include <util/system/fs.h>

#include <algorithm>

#ifdef _win_
    #include <aclapi.h>
#endif

namespace {
    /// empty directory for test that needs filesystem
    /// recreates directory in constructor and removes directory in destructor
    class TTestDirectory {
    private:
        TFsPath Path_;

    public:
        TTestDirectory(const TString& name);
        ~TTestDirectory();

        TFsPath GetFsPath() const {
            return Path_;
        }

        TFsPath Child(const TString& name) const {
            return Path_.Child(name);
        }
    };

    TTestDirectory::TTestDirectory(const TString& name) {
        Y_VERIFY(name.length() > 0, "have to specify name");
        Y_VERIFY(name.find('.') == TString::npos, "must be simple name");
        Y_VERIFY(name.find('/') == TString::npos, "must be simple name");
        Y_VERIFY(name.find('\\') == TString::npos, "must be simple name");
        Path_ = TFsPath(name);

        Path_.ForceDelete();
        Path_.MkDir();
    }

    TTestDirectory::~TTestDirectory() {
        Path_.ForceDelete();
    }
}

Y_UNIT_TEST_SUITE(TFsPathTests) {
    Y_UNIT_TEST(TestMkDirs) {
        const TFsPath path = "a/b/c/d/e/f";
        path.ForceDelete();
        TFsPath current = path;
        ui32 checksCounter = 0;
        while (current != ".") {
            UNIT_ASSERT(!path.Exists());
            ++checksCounter;
            current = current.Parent();
        }
        UNIT_ASSERT_VALUES_EQUAL(checksCounter, 6);

        path.MkDirs();
        UNIT_ASSERT(path.Exists());

        current = path;
        while (current != ".") {
            UNIT_ASSERT(path.Exists());
            current = current.Parent();
        }
    }

    Y_UNIT_TEST(MkDirFreak) {
        TFsPath path;
        UNIT_ASSERT_EXCEPTION(path.MkDir(), TIoException);
        UNIT_ASSERT_EXCEPTION(path.MkDirs(), TIoException);
        path = ".";
        path.MkDir();
        path.MkDirs();
    }

    Y_UNIT_TEST(Parent) {
#ifdef _win_
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("\\etc/passwd").Parent(), TFsPath("\\etc"));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("\\etc").Parent(), TFsPath("\\"));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("\\").Parent(), TFsPath("\\"));

        UNIT_ASSERT_VALUES_EQUAL(TFsPath("etc\\passwd").Parent(), TFsPath("etc"));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("etc").Parent(), TFsPath("."));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath(".\\etc").Parent(), TFsPath("."));

        UNIT_ASSERT_VALUES_EQUAL(TFsPath("C:\\etc/passwd").Parent(), TFsPath("C:\\etc"));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("C:\\etc").Parent(), TFsPath("C:\\"));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("C:\\").Parent(), TFsPath("C:\\"));
#else
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("/etc/passwd").Parent(), TFsPath("/etc"));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("/etc").Parent(), TFsPath("/"));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("/").Parent(), TFsPath("/"));

        UNIT_ASSERT_VALUES_EQUAL(TFsPath("etc/passwd").Parent(), TFsPath("etc"));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("etc").Parent(), TFsPath("."));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("./etc").Parent(), TFsPath("."));
#endif

#if 0
            UNIT_ASSERT_VALUES_EQUAL(TFsPath("./etc/passwd").Parent(), TFsPath("./etc"));
            UNIT_ASSERT_VALUES_EQUAL(TFsPath("./").Parent(), TFsPath(".."));
            UNIT_ASSERT_VALUES_EQUAL(TFsPath(".").Parent(), TFsPath(".."));
            UNIT_ASSERT_VALUES_EQUAL(TFsPath("..").Parent(), TFsPath("../.."));
#endif
    }

    Y_UNIT_TEST(GetName) {
        TTestDirectory d("GetName");
        UNIT_ASSERT_VALUES_EQUAL(TString("dfgh"), d.Child("dfgh").GetName());

        // check does not fail
        TFsPath(".").GetName();

#ifdef _unix_
        UNIT_ASSERT_VALUES_EQUAL(TString("/"), TFsPath("/").GetName());
#endif
    }

    Y_UNIT_TEST(GetExtension) {
        TTestDirectory d("GetExtension");
        UNIT_ASSERT_VALUES_EQUAL("", d.Child("a").GetExtension());
        UNIT_ASSERT_VALUES_EQUAL("", d.Child(".a").GetExtension());
        UNIT_ASSERT_VALUES_EQUAL("", d.Child("zlib").GetExtension());
        UNIT_ASSERT_VALUES_EQUAL("zlib", d.Child("file.zlib").GetExtension());
        UNIT_ASSERT_VALUES_EQUAL("zlib", d.Child("file.ylib.zlib").GetExtension());
    }

    Y_UNIT_TEST(TestRename) {
        TTestDirectory xx("TestRename");
        TFsPath f1 = xx.Child("f1");
        TFsPath f2 = xx.Child("f2");
        f1.Touch();
        f1.RenameTo(f2);
        UNIT_ASSERT(!f1.Exists());
        UNIT_ASSERT(f2.Exists());
    }

    Y_UNIT_TEST(TestForceRename) {
        TTestDirectory xx("TestForceRename");
        TFsPath fMain = xx.Child("main");

        TFsPath f1 = fMain.Child("f1");
        f1.MkDirs();
        TFsPath f1Child = f1.Child("f1child");
        f1Child.Touch();

        TFsPath f2 = fMain.Child("f2");
        f2.MkDirs();

        fMain.ForceRenameTo("TestForceRename/main1");

        UNIT_ASSERT(!xx.Child("main").Exists());
        UNIT_ASSERT(xx.Child("main1").Child("f1").Exists());
        UNIT_ASSERT(xx.Child("main1").Child("f2").Exists());
        UNIT_ASSERT(xx.Child("main1").Child("f1").Child("f1child").Exists());
    }

    Y_UNIT_TEST(TestRenameFail) {
        UNIT_ASSERT_EXCEPTION(TFsPath("sfsfsfsdfsfsdfdf").RenameTo("sdfsdf"), TIoException);
    }

#ifndef _win_
    Y_UNIT_TEST(TestRealPath) {
        UNIT_ASSERT(TFsPath(".").RealPath().IsDirectory());

        TTestDirectory td("TestRealPath");
        TFsPath link = td.Child("link");
        TFsPath target1 = td.Child("target1");
        target1.Touch();
        TFsPath target2 = td.Child("target2");
        target2.Touch();
        UNIT_ASSERT(NFs::SymLink(target1.RealPath(), link.GetPath()));
        UNIT_ASSERT_VALUES_EQUAL(link.RealPath(), target1.RealPath());
        UNIT_ASSERT(NFs::Remove(link.GetPath()));
        UNIT_ASSERT(NFs::SymLink(target2.RealPath(), link.GetPath()));
        UNIT_ASSERT_VALUES_EQUAL(link.RealPath(), target2.RealPath()); // must not cache old value
    }
#endif

    Y_UNIT_TEST(TestSlashesAndBasename) {
        TFsPath p("/db/BASE/primus121-025-1380131338//");
        UNIT_ASSERT_VALUES_EQUAL(p.Basename(), TString("primus121-025-1380131338"));
        TFsPath testP = p / "test";
#ifdef _win_
        UNIT_ASSERT_VALUES_EQUAL(testP.GetPath(), "\\db\\BASE\\primus121-025-1380131338\\test");
#else
        UNIT_ASSERT_VALUES_EQUAL(testP.GetPath(), "/db/BASE/primus121-025-1380131338/test");
#endif
    }

    Y_UNIT_TEST(TestSlashesAndBasenameWin) {
        TFsPath p("\\db\\BASE\\primus121-025-1380131338\\\\");
        TFsPath testP = p / "test";
#ifdef _win_
        UNIT_ASSERT_VALUES_EQUAL(p.Basename(), TString("primus121-025-1380131338"));
        UNIT_ASSERT_VALUES_EQUAL(testP.GetPath(), "\\db\\BASE\\primus121-025-1380131338\\test");
#else
        UNIT_ASSERT_VALUES_EQUAL(p.Basename(), TString("\\db\\BASE\\primus121-025-1380131338\\\\"));
        UNIT_ASSERT_VALUES_EQUAL(testP.GetPath(), "\\db\\BASE\\primus121-025-1380131338\\\\/test");
#endif
    }

    Y_UNIT_TEST(TestSlashesAndBasenameWinDrive) {
        TFsPath p("C:\\db\\BASE\\primus121-025-1380131338\\\\");
        TFsPath testP = p / "test";
#ifdef _win_
        UNIT_ASSERT_VALUES_EQUAL(p.Basename(), TString("primus121-025-1380131338"));
        UNIT_ASSERT_VALUES_EQUAL(testP.GetPath(), "C:\\db\\BASE\\primus121-025-1380131338\\test");
#else
        UNIT_ASSERT_VALUES_EQUAL(p.Basename(), TString("C:\\db\\BASE\\primus121-025-1380131338\\\\"));
        UNIT_ASSERT_VALUES_EQUAL(testP.GetPath(), "C:\\db\\BASE\\primus121-025-1380131338\\\\/test");
#endif
    }

    Y_UNIT_TEST(TestList) {
        TTestDirectory td("TestList-dir");

        TFsPath dir = td.GetFsPath();
        dir.Child("a").Touch();
        dir.Child("b").MkDir();
        dir.Child("b").Child("b-1").Touch();
        dir.Child("c").MkDir();
        dir.Child("d").Touch();

        TVector<TString> children;
        dir.ListNames(children);
        std::sort(children.begin(), children.end());

        TVector<TString> expected;
        expected.push_back("a");
        expected.push_back("b");
        expected.push_back("c");
        expected.push_back("d");

        UNIT_ASSERT_VALUES_EQUAL(expected, children);
    }

#ifdef _unix_
    Y_UNIT_TEST(MkDirMode) {
        TTestDirectory td("MkDirMode");
        TFsPath subDir = td.Child("subdir");
        const int mode = MODE0775;
        subDir.MkDir(mode);
        TFileStat stat;
        UNIT_ASSERT(subDir.Stat(stat));
        // mkdir(2) places umask(2) on mode argument.
        const int mask = Umask(0);
        Umask(mask);
        UNIT_ASSERT_VALUES_EQUAL(stat.Mode& MODE0777, mode & ~mask);
    }
#endif

    Y_UNIT_TEST(Cwd) {
        UNIT_ASSERT_VALUES_EQUAL(TFsPath::Cwd().RealPath(), TFsPath(".").RealPath());
    }

    Y_UNIT_TEST(TestSubpathOf) {
        UNIT_ASSERT(TFsPath("/a/b/c/d").IsSubpathOf("/a/b"));

        UNIT_ASSERT(TFsPath("/a").IsSubpathOf("/"));
        UNIT_ASSERT(!TFsPath("/").IsSubpathOf("/a"));
        UNIT_ASSERT(!TFsPath("/a").IsSubpathOf("/a"));

        UNIT_ASSERT(TFsPath("/a/b").IsSubpathOf("/a"));
        UNIT_ASSERT(TFsPath("a/b").IsSubpathOf("a"));
        UNIT_ASSERT(!TFsPath("/a/b").IsSubpathOf("/b"));
        UNIT_ASSERT(!TFsPath("a/b").IsSubpathOf("b"));

        // mixing absolute/relative
        UNIT_ASSERT(!TFsPath("a").IsSubpathOf("/"));
        UNIT_ASSERT(!TFsPath("a").IsSubpathOf("/a"));
        UNIT_ASSERT(!TFsPath("/a").IsSubpathOf("a"));
        UNIT_ASSERT(!TFsPath("a/b").IsSubpathOf("/a"));
        UNIT_ASSERT(!TFsPath("/a/b").IsSubpathOf("a"));

#ifdef _win_
        UNIT_ASSERT(TFsPath("x:/a/b").IsSubpathOf("x:/a"));
        UNIT_ASSERT(!TFsPath("x:/a/b").IsSubpathOf("y:/a"));
        UNIT_ASSERT(!TFsPath("x:/a/b").IsSubpathOf("a"));
#endif
    }

    Y_UNIT_TEST(TestNonStrictSubpathOf) {
        UNIT_ASSERT(TFsPath("/a/b/c/d").IsNonStrictSubpathOf("/a/b"));

        UNIT_ASSERT(TFsPath("/a").IsNonStrictSubpathOf("/"));
        UNIT_ASSERT(!TFsPath("/").IsNonStrictSubpathOf("/a"));

        UNIT_ASSERT(TFsPath("/a/b").IsNonStrictSubpathOf("/a"));
        UNIT_ASSERT(TFsPath("a/b").IsNonStrictSubpathOf("a"));
        UNIT_ASSERT(!TFsPath("/a/b").IsNonStrictSubpathOf("/b"));
        UNIT_ASSERT(!TFsPath("a/b").IsNonStrictSubpathOf("b"));

        // mixing absolute/relative
        UNIT_ASSERT(!TFsPath("a").IsNonStrictSubpathOf("/"));
        UNIT_ASSERT(!TFsPath("a").IsNonStrictSubpathOf("/a"));
        UNIT_ASSERT(!TFsPath("/a").IsNonStrictSubpathOf("a"));
        UNIT_ASSERT(!TFsPath("a/b").IsNonStrictSubpathOf("/a"));
        UNIT_ASSERT(!TFsPath("/a/b").IsNonStrictSubpathOf("a"));

        // equal paths
        UNIT_ASSERT(TFsPath("").IsNonStrictSubpathOf(""));
        UNIT_ASSERT(TFsPath("/").IsNonStrictSubpathOf("/"));
        UNIT_ASSERT(TFsPath("a").IsNonStrictSubpathOf("a"));
        UNIT_ASSERT(TFsPath("/a").IsNonStrictSubpathOf("/a"));
        UNIT_ASSERT(TFsPath("/a").IsNonStrictSubpathOf("/a/"));
        UNIT_ASSERT(TFsPath("/a/").IsNonStrictSubpathOf("/a"));
        UNIT_ASSERT(TFsPath("/a/").IsNonStrictSubpathOf("/a/"));

#ifdef _win_
        UNIT_ASSERT(TFsPath("x:/a/b").IsNonStrictSubpathOf("x:/a"));

        UNIT_ASSERT(TFsPath("x:/a").IsNonStrictSubpathOf("x:/a"));
        UNIT_ASSERT(TFsPath("x:/a/").IsNonStrictSubpathOf("x:/a"));
        UNIT_ASSERT(TFsPath("x:/a").IsNonStrictSubpathOf("x:/a/"));
        UNIT_ASSERT(TFsPath("x:/a/").IsNonStrictSubpathOf("x:/a/"));

        UNIT_ASSERT(!TFsPath("x:/").IsNonStrictSubpathOf("y:/"));
        UNIT_ASSERT(!TFsPath("x:/a/b").IsNonStrictSubpathOf("y:/a"));
        UNIT_ASSERT(!TFsPath("x:/a/b").IsNonStrictSubpathOf("a"));
#endif
    }

    Y_UNIT_TEST(TestRelativePath) {
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("/a/b/c/d").RelativePath(TFsPath("/a/b")), TFsPath("c/d"));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("/a/b/c/d").RelativePath(TFsPath("/a/b/e/f")), TFsPath("../../c/d"));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("/").RelativePath(TFsPath("/")), TFsPath());
        UNIT_ASSERT_VALUES_EQUAL(TFsPath(".").RelativePath(TFsPath(".")), TFsPath());
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("/a/c").RelativePath(TFsPath("/a/b/../c")), TFsPath());
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("a/.././b").RelativePath(TFsPath("b/c")), TFsPath(".."));

        UNIT_ASSERT_EXCEPTION(TFsPath("a/b/c").RelativePath(TFsPath("d/e")), TIoException);
    }

    Y_UNIT_TEST(TestUndefined) {
        UNIT_ASSERT_VALUES_EQUAL(TFsPath(), TFsPath(""));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath(), TFsPath().Fix());

        UNIT_ASSERT_VALUES_EQUAL(TFsPath() / TFsPath(), TFsPath());
#ifdef _win_
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("a\\b"), TFsPath() / TString("a\\b"));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("a\\b"), "a\\b" / TFsPath());
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("\\a\\b"), TFsPath() / "\\a\\b");
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("\\a\\b"), "\\a\\b" / TFsPath());
#else
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("a/b"), TFsPath() / TString("a/b"));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("a/b"), "a/b" / TFsPath());
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("/a/b"), TFsPath() / "/a/b");
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("/a/b"), "/a/b" / TFsPath());
#endif
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("."), TFsPath() / ".");
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("."), "." / TFsPath());

        UNIT_ASSERT(TFsPath().PathSplit().empty());
        UNIT_ASSERT(!TFsPath().PathSplit().IsAbsolute);
        UNIT_ASSERT(TFsPath().IsRelative()); // undefined path is relative

        UNIT_ASSERT_VALUES_EQUAL(TFsPath().GetPath(), "");
        UNIT_ASSERT_VALUES_EQUAL(TFsPath().GetName(), "");
        UNIT_ASSERT_VALUES_EQUAL(TFsPath().GetExtension(), "");

        UNIT_ASSERT_VALUES_EQUAL(TFsPath().Parent(), TFsPath());
        UNIT_ASSERT_VALUES_EQUAL(TFsPath().Child("a"), TFsPath("a"));
        UNIT_ASSERT_VALUES_EQUAL(TFsPath().Basename(), "");
        UNIT_ASSERT_VALUES_EQUAL(TFsPath().Dirname(), "");

        UNIT_ASSERT(!TFsPath().IsSubpathOf("a/b"));
        UNIT_ASSERT(TFsPath().IsContainerOf("a/b"));
        UNIT_ASSERT(!TFsPath().IsContainerOf("/a/b"));
#ifdef _win_
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("a\\b").RelativeTo(TFsPath()), TFsPath("a\\b"));
#else
        UNIT_ASSERT_VALUES_EQUAL(TFsPath("a/b").RelativeTo(TFsPath()), TFsPath("a/b"));
#endif

        UNIT_ASSERT(!TFsPath().Exists());
        UNIT_ASSERT(!TFsPath().IsFile());
        UNIT_ASSERT(!TFsPath().IsDirectory());
        TFileStat stat;
        UNIT_ASSERT(!TFsPath().Stat(stat));
    }

    Y_UNIT_TEST(TestJoinFsPaths) {
#ifdef _win_
        UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a\\b", "c\\d"), "a\\b\\c\\d");
        UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a\\b", "..\\c"), "a\\b\\..\\c");
        UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a\\b\\..\\c", "d"), "a\\c\\d");
        UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a", "b", "c", "d"), "a\\b\\c\\d");
        UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a\\b\\..\\c"), "a\\b\\..\\c");
        UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a\\b", ""), "a\\b");
#else
        UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a/b", "c/d"), "a/b/c/d");
        UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a/b", "../c"), "a/b/../c");
        UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a/b/../c", "d"), "a/c/d");
        UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a", "b", "c", "d"), "a/b/c/d");
        UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a/b/../c"), "a/b/../c");
        UNIT_ASSERT_VALUES_EQUAL(JoinFsPaths("a/b", ""), "a/b");
#endif
    }

    Y_UNIT_TEST(TestStringCast) {
        TFsPath pathOne;
        UNIT_ASSERT(TryFromString<TFsPath>("/a/b", pathOne));
        UNIT_ASSERT_VALUES_EQUAL(pathOne, TFsPath{"/a/b"});

        TFsPath pathTwo;
        UNIT_ASSERT_NO_EXCEPTION(TryFromString<TFsPath>("/a/b", pathTwo));

        UNIT_ASSERT_VALUES_EQUAL(FromString<TFsPath>("/a/b"), TFsPath{"/a/b"});

        TFsPath pathThree{"/a/b"};
        UNIT_ASSERT_VALUES_EQUAL(ToString(pathThree), "/a/b");
    }

#ifdef _unix_
    Y_UNIT_TEST(TestRemoveSymlinkToDir) {
        TTempDir tempDir;
        TFsPath tempDirPath(tempDir());

        const TString originDir = tempDirPath.Child("origin");
        MakePathIfNotExist(originDir.c_str());

        const TString originFile = TFsPath(originDir).Child("data");
        {
            TFixedBufferFileOutput out(originFile);
            out << "data111!!!";
        }

        const TString link = tempDirPath.Child("origin_symlink");
        NFs::SymLink(originDir, link);

        TFsPath(link).ForceDelete();

        UNIT_ASSERT(!NFs::Exists(link));
        UNIT_ASSERT(NFs::Exists(originFile));
        UNIT_ASSERT(NFs::Exists(originDir));
    }

    Y_UNIT_TEST(TestRemoveSymlinkToFile) {
        TTempDir tempDir;
        TFsPath tempDirPath(tempDir());

        const TString originDir = tempDirPath.Child("origin");
        MakePathIfNotExist(originDir.c_str());

        const TString originFile = TFsPath(originDir).Child("data");
        {
            TFixedBufferFileOutput out(originFile);
            out << "data111!!!";
        }

        const TString link = tempDirPath.Child("origin_symlink");
        NFs::SymLink(originFile, link);

        TFsPath(link).ForceDelete();

        UNIT_ASSERT(!NFs::Exists(link));
        UNIT_ASSERT(NFs::Exists(originFile));
        UNIT_ASSERT(NFs::Exists(originDir));
    }

    Y_UNIT_TEST(TestRemoveDirWithSymlinkToDir) {
        TTempDir tempDir;
        TFsPath tempDirPath(tempDir());

        const TString symlinkedDir = tempDirPath.Child("to_remove");
        MakePathIfNotExist(symlinkedDir.c_str());

        const TString originDir = tempDirPath.Child("origin");
        MakePathIfNotExist(originDir.c_str());

        const TString originFile = TFsPath(originDir).Child("data");
        {
            TFixedBufferFileOutput out(originFile);
            out << "data111!!!";
        }

        const TString symlinkedFile = TFsPath(symlinkedDir).Child("origin_symlink");
        NFs::SymLink(originDir, symlinkedFile);

        TFsPath(symlinkedDir).ForceDelete();

        UNIT_ASSERT(!NFs::Exists(symlinkedFile));
        UNIT_ASSERT(!NFs::Exists(symlinkedDir));
        UNIT_ASSERT(NFs::Exists(originFile));
        UNIT_ASSERT(NFs::Exists(originDir));
    }

    Y_UNIT_TEST(TestRemoveDirWithSymlinkToFile) {
        TTempDir tempDir;
        TFsPath tempDirPath(tempDir());

        const TString symlinkedDir = tempDirPath.Child("to_remove");
        MakePathIfNotExist(symlinkedDir.c_str());

        const TString originDir = tempDirPath.Child("origin");
        MakePathIfNotExist(originDir.c_str());

        const TString originFile = TFsPath(originDir).Child("data");
        {
            TFixedBufferFileOutput out(originFile);
            out << "data111!!!";
        }

        const TString symlinkedFile = TFsPath(symlinkedDir).Child("origin_symlink");
        NFs::SymLink(originFile, symlinkedFile);

        TFsPath(symlinkedDir).ForceDelete();

        UNIT_ASSERT(!NFs::Exists(symlinkedFile));
        UNIT_ASSERT(!NFs::Exists(symlinkedDir));
        UNIT_ASSERT(NFs::Exists(originFile));
        UNIT_ASSERT(NFs::Exists(originDir));
    }
#endif

    Y_UNIT_TEST(TestForceDeleteNonexisting) {
        TTempDir tempDir;
        TFsPath nonexisting = TFsPath(tempDir()).Child("nonexisting");
        nonexisting.ForceDelete();
    }

    // Here we want to test that all possible errors during TFsPath::ForceDelete
    // are properly handled. To do so we have to trigger fs operation errors in
    // three points:
    // 1. stat/GetFileInformationByHandle
    // 2. opendir
    // 3. unlink/rmdir
    //
    // On unix systems we can achieve this by simply setting access rights on
    // entry being deleted and its parent. But on windows it is more complicated.
    // Current Chmod implementation on windows is not enough as it sets only
    // FILE_ATTRIBUTE_READONLY throught SetFileAttributes call. But doing so does
    // not affect directory access rights on older versions of Windows and Wine
    // that we use to run autocheck tests.
    //
    // To get required access rights we use DACL in SetSecurityInfo. This is wrapped
    // in RAII class that drops requested permissions on file/dir and grantss them
    // back in destructor.
    //
    // Another obstacle is FILE_LIST_DIRECTORY permission when running on Wine.
    // Dropping this permission is necessary to provoke error
    // in GetFileInformationByHandle. Wine allows dropping this permission, but I
    // have not found a way to grant it back. So tests crash during cleanup sequence.
    // To make it possible to run this tests natively we detect Wine with special
    // registry key and skip these tests only there.

#ifdef _win_
    struct TLocalFree {
        static void Destroy(void* ptr) {
            LocalFree((HLOCAL)ptr);
        }
    };

    bool IsWine() {
        HKEY subKey = nullptr;
        LONG result = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Wine", 0, KEY_READ, &subKey);
        if (result == ERROR_SUCCESS) {
            return true;
        }
        result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, KEY_READ, &subKey);
        if (result == ERROR_SUCCESS) {
            return true;
        }

        HMODULE hntdll = GetModuleHandle("ntdll.dll");
        if (!hntdll) {
            return false;
        }

        auto func = GetProcAddress(hntdll, "wine_get_version");
        return func != nullptr;
    }

    class TWinFileDenyAccessScope {
    public:
        TWinFileDenyAccessScope(const TFsPath& name, DWORD permissions)
            : Name_(name)
            , Perms_(permissions)
        {
            DWORD res = 0;
            PACL oldAcl = nullptr;
            PSECURITY_DESCRIPTOR sd = nullptr;

            res = GetNamedSecurityInfoA((LPSTR)name.c_str(),
                                        SE_FILE_OBJECT,
                                        DACL_SECURITY_INFORMATION,
                                        nullptr,
                                        nullptr,
                                        &oldAcl,
                                        nullptr,
                                        &sd);
            SdHolder_.Reset(sd);
            if (res != ERROR_SUCCESS) {
                ythrow TSystemError(res) << "error in GetNamedSecurityInfoA";
            }

            Acl_ = SetAcl(oldAcl, DENY_ACCESS);
        }

        ~TWinFileDenyAccessScope() {
            try {
                const TFsPath parent = Name_.Parent();
                Chmod(parent.c_str(), MODE0777);
                Chmod(Name_.c_str(), MODE0777);
                SetAcl((PACL)Acl_.Get(), GRANT_ACCESS);
            } catch (const yexception& ex) {
                Cerr << "~TWinFileDenyAccessScope failed: " << ex.AsStrBuf() << Endl;
            }
        }

        THolder<void, TLocalFree> SetAcl(PACL oldAcl, ACCESS_MODE accessMode) {
            DWORD res = 0;
            EXPLICIT_ACCESS ea;
            PACL newAcl = nullptr;
            THolder<void, TLocalFree> newAclHolder;

            memset(&ea, 0, sizeof(EXPLICIT_ACCESS));
            ea.grfAccessPermissions = Perms_;
            ea.grfAccessMode = accessMode;
            ea.grfInheritance = NO_INHERITANCE;
            ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
            ea.Trustee.ptstrName = (LPSTR) "CURRENT_USER";

            res = SetEntriesInAcl(1, &ea, oldAcl, &newAcl);
            newAclHolder.Reset(newAcl);
            if (res != ERROR_SUCCESS) {
                ythrow TSystemError(res) << "error in SetEntriesInAcl";
            }

            res = SetNamedSecurityInfoA((LPSTR)Name_.c_str(),
                                        SE_FILE_OBJECT,
                                        DACL_SECURITY_INFORMATION,
                                        nullptr,
                                        nullptr,
                                        newAcl,
                                        nullptr);
            if (res != ERROR_SUCCESS) {
                ythrow TSystemError(res) << "error in SetNamedSecurityInfoA";
            }

            return std::move(newAclHolder);
        }

    private:
        const TFsPath Name_;
        const DWORD Perms_;
        THolder<void, TLocalFree> SdHolder_;
        THolder<void, TLocalFree> Acl_;
    };
#endif

    Y_UNIT_TEST(TestForceDeleteErrorUnlink) {
        TTempDir tempDir;

        const TFsPath testDir = TFsPath(tempDir()).Child("dir");
        MakePathIfNotExist(testDir.c_str());

        const TFsPath testFile = testDir.Child("file");
        {
            TFixedBufferFileOutput out(testFile);
            out << "data111!!!";
        }

#ifdef _win_
        Chmod(testFile.c_str(), S_IRUSR);
        Y_DEFER {
            Chmod(testFile.c_str(), MODE0777);
        };
#else
        Chmod(testDir.c_str(), S_IRUSR | S_IXUSR);
        Y_DEFER {
            Chmod(testDir.c_str(), MODE0777);
        };
#endif

        UNIT_ASSERT_EXCEPTION_CONTAINS(testFile.ForceDelete(), TIoException, "failed to delete");
    }

    Y_UNIT_TEST(TestForceDeleteErrorRmdir) {
        TTempDir tempDir;

        const TFsPath testDir = TFsPath(tempDir()).Child("dir");
        const TFsPath testSubdir = testDir.Child("file");
        MakePathIfNotExist(testSubdir.c_str());

#ifdef _win_
        Chmod(testSubdir.c_str(), 0);
        Y_DEFER {
            Chmod(testSubdir.c_str(), MODE0777);
        };
        TWinFileDenyAccessScope dirAcl(testDir, FILE_WRITE_DATA);
#else
        Chmod(testDir.c_str(), S_IRUSR | S_IXUSR);
        Y_DEFER {
            Chmod(testDir.c_str(), MODE0777);
        };
#endif

        UNIT_ASSERT_EXCEPTION_CONTAINS(testSubdir.ForceDelete(), TIoException, "failed to delete");
    }

    Y_UNIT_TEST(TestForceDeleteErrorStatDir) {
        TTempDir tempDir;

        const TFsPath testDir = TFsPath(tempDir()).Child("dir");
        const TFsPath testSubdir = testDir.Child("file");
        MakePathIfNotExist(testSubdir.c_str());

#ifdef _win_
        if (IsWine()) {
            // FILE_LIST_DIRECTORY seem to be irreversible on wine
            return;
        }
        TWinFileDenyAccessScope subdirAcl(testSubdir, FILE_READ_ATTRIBUTES);
        TWinFileDenyAccessScope dirAcl(testDir, FILE_LIST_DIRECTORY);
#else
        Chmod(testDir.c_str(), 0);
        Y_DEFER {
            Chmod(testDir.c_str(), MODE0777);
        };
#endif

        UNIT_ASSERT_EXCEPTION_CONTAINS(testSubdir.ForceDelete(), TIoException, "failed to stat");
    }

    Y_UNIT_TEST(TestForceDeleteErrorStatFile) {
        TTempDir tempDir;

        const TFsPath testDir = TFsPath(tempDir()).Child("dir");
        MakePathIfNotExist(testDir.c_str());

        const TFsPath testFile = testDir.Child("file");
        {
            TFixedBufferFileOutput out(testFile);
            out << "data111!!!";
        }

#ifdef _win_
        if (IsWine()) {
            // FILE_LIST_DIRECTORY seem to be irreversible on wine
            return;
        }
        TWinFileDenyAccessScope fileAcl(testFile, FILE_READ_ATTRIBUTES);
        TWinFileDenyAccessScope dirAcl(testDir, FILE_LIST_DIRECTORY);
#else
        Chmod(testDir.c_str(), 0);
        Y_DEFER {
            Chmod(testDir.c_str(), MODE0777);
        };
#endif

        UNIT_ASSERT_EXCEPTION_CONTAINS(testFile.ForceDelete(), TIoException, "failed to stat");
    }

    Y_UNIT_TEST(TestForceDeleteErrorListDir) {
        TTempDir tempDir;

        const TFsPath testDir = TFsPath(tempDir()).Child("dir");
        const TFsPath testSubdir = testDir.Child("file");
        MakePathIfNotExist(testSubdir.c_str());

#ifdef _win_
        if (IsWine()) {
            // FILE_LIST_DIRECTORY seem to be irreversible on wine
            return;
        }
        TWinFileDenyAccessScope subdirAcl(testSubdir, FILE_LIST_DIRECTORY);
#else
        Chmod(testSubdir.c_str(), 0);
        Y_DEFER {
            Chmod(testSubdir.c_str(), MODE0777);
        };
#endif

        UNIT_ASSERT_EXCEPTION_CONTAINS(testSubdir.ForceDelete(), TIoException, "failed to opendir");
    }

#ifdef _unix_
    Y_UNIT_TEST(TestForceDeleteErrorSymlink) {
        TTempDir tempDir;

        const TFsPath testDir = TFsPath(tempDir()).Child("dir");
        MakePathIfNotExist(testDir.c_str());

        const TFsPath testSymlink = testDir.Child("symlink");
        NFs::SymLink("something", testSymlink);

        Chmod(testSymlink.c_str(), S_IRUSR);
        Chmod(testDir.c_str(), S_IRUSR | S_IXUSR);
        Y_DEFER {
            Chmod(testDir.c_str(), MODE0777);
            Chmod(testSymlink.c_str(), MODE0777);
        };

        UNIT_ASSERT_EXCEPTION_CONTAINS(testSymlink.ForceDelete(), TIoException, "failed to delete");
    }
#endif

    Y_UNIT_TEST(TestCopyWithInitializedSplit) {
        const TFsPath path1 = TFsPath("some_folder_with_file") / TFsPath("file_in_folder");
        path1.PathSplit();

        const TFsPath path2 = path1;
        const TPathSplit& split2 = path2.PathSplit();

        for (const auto& it : split2) {
            UNIT_ASSERT(path2.GetPath().begin() <= it.begin());
            UNIT_ASSERT(it.end() <= path2.GetPath().end());
        }
    }

    Y_UNIT_TEST(TestAssignmentWithInitializedSplit) {
        TFsPath path1 = TFsPath("some_folder_with_file_1") / TFsPath("file_in_folder_1");
        TFsPath path2 = TFsPath("some_folder_with_file_2") / TFsPath("file_in_folder_2");
        path1.PathSplit();
        path1 = path2;
        UNIT_ASSERT_VALUES_EQUAL(path1.PathSplit().at(1), "file_in_folder_2");
    }
}