testsuite_fs.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // -*- C++ -*-
  2. // Filesystem utils for the C++ library testsuite.
  3. //
  4. // Copyright (C) 2014-2022 Free Software Foundation, Inc.
  5. //
  6. // This file is part of the GNU ISO C++ Library. This library is free
  7. // software; you can redistribute it and/or modify it under the
  8. // terms of the GNU General Public License as published by the
  9. // Free Software Foundation; either version 3, or (at your option)
  10. // any later version.
  11. //
  12. // This library is distributed in the hope that it will be useful,
  13. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. // GNU General Public License for more details.
  16. //
  17. // You should have received a copy of the GNU General Public License along
  18. // with this library; see the file COPYING3. If not see
  19. // <http://www.gnu.org/licenses/>.
  20. //
  21. #ifndef _TESTSUITE_FS_H
  22. #define _TESTSUITE_FS_H 1
  23. // Assume we want std::filesystem in C++17, unless USE_FILESYSTEM_TS defined:
  24. #if __cplusplus >= 201703L && ! defined USE_FILESYSTEM_TS
  25. #include <filesystem>
  26. namespace test_fs = std::filesystem;
  27. #else
  28. #include <experimental/filesystem>
  29. namespace test_fs = std::experimental::filesystem;
  30. #endif
  31. #include <algorithm>
  32. #include <fstream>
  33. #include <string>
  34. #include <cstdio>
  35. #include <unistd.h> // unlink, close, getpid, geteuid
  36. #if defined(_GNU_SOURCE) || _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L
  37. #include <stdlib.h> // mkstemp
  38. #else
  39. #include <random> // std::random_device
  40. #endif
  41. namespace __gnu_test
  42. {
  43. #define PATH_CHK(p1, p2, fn) \
  44. if ( p1.fn() != p2.fn() ) \
  45. throw test_fs::filesystem_error("comparing '" #fn "' failed", p1, p2, \
  46. std::make_error_code(std::errc::invalid_argument) )
  47. void
  48. compare_paths(const test_fs::path& p1,
  49. const test_fs::path& p2)
  50. {
  51. PATH_CHK( p1, p2, native );
  52. PATH_CHK( p1, p2, string );
  53. PATH_CHK( p1, p2, empty );
  54. PATH_CHK( p1, p2, has_root_path );
  55. PATH_CHK( p1, p2, has_root_name );
  56. PATH_CHK( p1, p2, has_root_directory );
  57. PATH_CHK( p1, p2, has_relative_path );
  58. PATH_CHK( p1, p2, has_parent_path );
  59. PATH_CHK( p1, p2, has_filename );
  60. PATH_CHK( p1, p2, has_stem );
  61. PATH_CHK( p1, p2, has_extension );
  62. PATH_CHK( p1, p2, is_absolute );
  63. PATH_CHK( p1, p2, is_relative );
  64. auto d1 = std::distance(p1.begin(), p1.end());
  65. auto d2 = std::distance(p2.begin(), p2.end());
  66. if (d1 != d2)
  67. throw test_fs::filesystem_error(
  68. "distance(begin1, end1) != distance(begin2, end2)", p1, p2,
  69. std::make_error_code(std::errc::invalid_argument) );
  70. if (!std::equal(p1.begin(), p1.end(), p2.begin()))
  71. throw test_fs::filesystem_error(
  72. "!equal(begin1, end1, begin2)", p1, p2,
  73. std::make_error_code(std::errc::invalid_argument) );
  74. }
  75. const std::string test_paths[] = {
  76. "", "/", "//", "/.", "/./", "/a", "/a/", "/a//", "/a/b/c/d", "/a//b",
  77. "a", "a/b", "a/b/", "a/b/c", "a/b/c.d", "a/b/..", "a/b/c.", "a/b/.c"
  78. };
  79. test_fs::path
  80. root_path()
  81. {
  82. #if defined(__MINGW32__) || defined(__MINGW64__)
  83. return L"c:/";
  84. #else
  85. return "/";
  86. #endif
  87. }
  88. // This is NOT supposed to be a secure way to get a unique name!
  89. // We just need a path that doesn't exist for testing purposes.
  90. test_fs::path
  91. nonexistent_path(std::string file = __builtin_FILE())
  92. {
  93. // Include the caller's filename to help identify tests that fail to
  94. // clean up the files they create.
  95. // Remove .cc extension:
  96. if (file.length() > 3 && file.compare(file.length() - 3, 3, ".cc") == 0)
  97. file.resize(file.length() - 3);
  98. // And directory:
  99. auto pos = file.find_last_of("/\\");
  100. if (pos != file.npos)
  101. file.erase(0, pos+1);
  102. test_fs::path p;
  103. #if defined(_GNU_SOURCE) || _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L
  104. char tmp[] = "filesystem-test.XXXXXX";
  105. int fd = ::mkstemp(tmp);
  106. if (fd == -1)
  107. throw test_fs::filesystem_error("mkstemp failed",
  108. std::error_code(errno, std::generic_category()));
  109. ::unlink(tmp);
  110. ::close(fd);
  111. if (!file.empty())
  112. file.insert(0, 1, '-');
  113. file.insert(0, tmp);
  114. p = file;
  115. #else
  116. if (file.length() > 64)
  117. file.resize(64);
  118. char buf[128];
  119. static unsigned counter = std::random_device{}();
  120. #if _GLIBCXX_USE_C99_STDIO
  121. std::snprintf(buf, 128,
  122. #else
  123. std::sprintf(buf,
  124. #endif
  125. "filesystem-test.%u.%lu-%s", counter++, (unsigned long) ::getpid(),
  126. file.c_str());
  127. p = buf;
  128. #endif
  129. return p;
  130. }
  131. // RAII helper to remove a file on scope exit.
  132. struct scoped_file
  133. {
  134. using path_type = test_fs::path;
  135. enum adopt_file_t { adopt_file };
  136. explicit
  137. scoped_file(const path_type& p = nonexistent_path()) : path(p)
  138. { std::ofstream{p.c_str()}; }
  139. scoped_file(path_type p, adopt_file_t) : path(p) { }
  140. ~scoped_file() { if (!path.empty()) remove(path); }
  141. scoped_file(scoped_file&&) = default;
  142. scoped_file& operator=(scoped_file&&) = default;
  143. path_type path;
  144. };
  145. inline bool
  146. permissions_are_testable(bool print_msg = true)
  147. {
  148. bool testable = false;
  149. #if !(defined __MINGW32__ || defined __MINGW64__)
  150. if (geteuid() != 0)
  151. testable = true;
  152. // XXX on Linux the CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH capabilities
  153. // can give normal users extra permissions for files and directories.
  154. // We ignore that possibility here.
  155. #endif
  156. if (print_msg && !testable)
  157. std::puts("Skipping tests that depend on filesystem permissions");
  158. return testable;
  159. }
  160. } // namespace __gnu_test
  161. #endif