fts_ut.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. #include "fts.h"
  2. #include "dirut.h"
  3. #include "tempdir.h"
  4. #include <library/cpp/testing/unittest/registar.h>
  5. #include <library/cpp/threading/future/async.h>
  6. #include <util/system/file.h>
  7. #include <util/system/tempfile.h>
  8. #include <util/generic/string.h>
  9. class TFtsTest: public TTestBase {
  10. UNIT_TEST_SUITE(TFtsTest);
  11. UNIT_TEST(TestSimple);
  12. UNIT_TEST(TestNoLeakChangingAccessToFolder);
  13. UNIT_TEST_SUITE_END();
  14. public:
  15. void TestSimple();
  16. void TestNoLeakChangingAccessToFolder();
  17. };
  18. void MakeFile(const char* path) {
  19. TFile(path, CreateAlways);
  20. }
  21. // There potentially could be problems in listing order on different platforms
  22. int FtsCmp(const FTSENT** ent1, const FTSENT** ent2) {
  23. return strcmp((*ent1)->fts_accpath, (*ent2)->fts_accpath);
  24. }
  25. void CheckEnt(FTSENT* ent, const char* name, int type) {
  26. UNIT_ASSERT(ent);
  27. UNIT_ASSERT_STRINGS_EQUAL(ent->fts_path, name);
  28. UNIT_ASSERT_EQUAL(ent->fts_info, type);
  29. }
  30. class TFileTree {
  31. public:
  32. TFileTree(char* const* argv, int options, int (*compar)(const FTSENT**, const FTSENT**)) {
  33. Fts_ = yfts_open(argv, options, compar);
  34. }
  35. ~TFileTree() {
  36. yfts_close(Fts_);
  37. }
  38. FTS* operator()() {
  39. return Fts_;
  40. }
  41. private:
  42. FTS* Fts_;
  43. };
  44. void TFtsTest::TestSimple() {
  45. const char* dotPath[2] = {"." LOCSLASH_S, nullptr};
  46. TFileTree currentDirTree((char* const*)dotPath, 0, FtsCmp);
  47. UNIT_ASSERT(currentDirTree());
  48. TTempDir tempDir = MakeTempName(yfts_read(currentDirTree())->fts_path);
  49. MakeDirIfNotExist(tempDir().data());
  50. MakeDirIfNotExist((tempDir() + LOCSLASH_S "dir1").data());
  51. MakeFile((tempDir() + LOCSLASH_S "dir1" LOCSLASH_S "file1").data());
  52. MakeFile((tempDir() + LOCSLASH_S "dir1" LOCSLASH_S "file2").data());
  53. MakeDirIfNotExist((tempDir() + LOCSLASH_S "dir2").data());
  54. MakeFile((tempDir() + LOCSLASH_S "dir2" LOCSLASH_S "file3").data());
  55. MakeFile((tempDir() + LOCSLASH_S "dir2" LOCSLASH_S "file4").data());
  56. const char* path[2] = {tempDir().data(), nullptr};
  57. TFileTree fileTree((char* const*)path, 0, FtsCmp);
  58. UNIT_ASSERT(fileTree());
  59. CheckEnt(yfts_read(fileTree()), tempDir().data(), FTS_D);
  60. CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir1").data(), FTS_D);
  61. CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir1" LOCSLASH_S "file1").data(), FTS_F);
  62. CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir1" LOCSLASH_S "file2").data(), FTS_F);
  63. CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir1").data(), FTS_DP);
  64. CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir2").data(), FTS_D);
  65. CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir2" LOCSLASH_S "file3").data(), FTS_F);
  66. CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir2" LOCSLASH_S "file4").data(), FTS_F);
  67. CheckEnt(yfts_read(fileTree()), (tempDir() + LOCSLASH_S "dir2").data(), FTS_DP);
  68. CheckEnt(yfts_read(fileTree()), (tempDir()).data(), FTS_DP);
  69. UNIT_ASSERT_EQUAL(yfts_read(fileTree()), nullptr);
  70. }
  71. class TTempDirWithLostAccess: public TTempDir {
  72. public:
  73. ~TTempDirWithLostAccess() {
  74. chmod(Name().data(), 0777);
  75. }
  76. };
  77. // https://st.yandex-team.ru/YQ-318
  78. // Test that detects memory leak in case of error in chdir in fts_build function.
  79. void TFtsTest::TestNoLeakChangingAccessToFolder() {
  80. TTempDirWithLostAccess tempDir;
  81. TString tmpPath = tempDir();
  82. if (tmpPath.EndsWith(LOCSLASH_S)) {
  83. tmpPath.resize(tmpPath.size() - 1);
  84. }
  85. MakeDirIfNotExist((tmpPath + LOCSLASH_S + "subdir").data());
  86. const char* path[2] = {tmpPath.data(), nullptr};
  87. TFileTree fileTree((char* const*)path, FTS_SEEDOT, FtsCmp);
  88. UNIT_ASSERT(fileTree());
  89. CheckEnt(yfts_read(fileTree()), tmpPath.data(), FTS_D);
  90. #ifndef _win32_
  91. CheckEnt(yfts_read(fileTree()), (tmpPath + LOCSLASH_S ".").data(), FTS_DOT);
  92. #endif // _win32_
  93. CheckEnt(yfts_read(fileTree()), (tmpPath + LOCSLASH_S "..").data(), FTS_DOT);
  94. CheckEnt(yfts_read(fileTree()), (tmpPath + LOCSLASH_S "subdir").data(), FTS_D);
  95. auto pool = CreateThreadPool(2);
  96. auto chmodFuture = NThreading::Async([name = tmpPath] {
  97. UNIT_ASSERT_C(!chmod(name.data(), 0), "Errno: " << errno);
  98. }, *pool);
  99. auto childrenFuture = NThreading::Async([&] {
  100. yfts_children(fileTree(), 0);
  101. }, *pool);
  102. childrenFuture.Wait();
  103. chmodFuture.Wait();
  104. }
  105. UNIT_TEST_SUITE_REGISTRATION(TFtsTest);