]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - libstdc++-v3/testsuite/util/testsuite_fs.h
Update copyright years.
[thirdparty/gcc.git] / libstdc++-v3 / testsuite / util / testsuite_fs.h
index 38ebd4fb07970b684529ad56be001ce3af31a22b..f26ad7297c622af56747d2208ae2f740bbad432a 100644 (file)
@@ -1,7 +1,7 @@
 // -*- C++ -*-
 // Filesystem utils for the C++ library testsuite.
 //
-// Copyright (C) 2014-2017 Free Software Foundation, Inc.
+// Copyright (C) 2014-2024 Free Software Foundation, Inc.
 //
 // This file is part of the GNU ISO C++ Library.  This library is free
 // software; you can redistribute it and/or modify it under the
 #ifndef _TESTSUITE_FS_H
 #define _TESTSUITE_FS_H 1
 
+// Assume we want std::filesystem in C++17, unless USE_FILESYSTEM_TS defined:
+#if __cplusplus >= 201703L && ! defined USE_FILESYSTEM_TS
+#include <filesystem>
+namespace test_fs = std::filesystem;
+#else
 #include <experimental/filesystem>
+namespace test_fs = std::experimental::filesystem;
+#endif
+#include <algorithm>
 #include <fstream>
+#include <random>   // std::random_device
 #include <string>
+#include <system_error>
 #include <cstdio>
-#include <stdlib.h>
-#include <unistd.h>
+#include <unistd.h> // unlink, close, getpid, geteuid
+
+#if defined(_GNU_SOURCE) || _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L
+#include <stdlib.h> // mkstemp
+#endif
+
+#ifndef _GLIBCXX_HAVE_SYMLINK
+#define NO_SYMLINKS
+#endif
+
+#if !defined (_GLIBCXX_HAVE_SYS_STATVFS_H) \
+  && !defined (_GLIBCXX_FILESYSTEM_IS_WINDOWS)
+#define NO_SPACE
+#endif
+
+#if !(_GLIBCXX_HAVE_SYS_STAT_H \
+      && (_GLIBCXX_USE_UTIMENSAT || _GLIBCXX_USE_UTIME))
+#define NO_LAST_WRITE_TIME 1
+#endif
 
 namespace __gnu_test
 {
 #define PATH_CHK(p1, p2, fn) \
     if ( p1.fn() != p2.fn() ) \
-      throw std::experimental::filesystem::filesystem_error( #fn, p1, p2, \
+      throw test_fs::filesystem_error("comparing '" #fn "' failed", p1, p2, \
          std::make_error_code(std::errc::invalid_argument) )
 
   void
-  compare_paths(const std::experimental::filesystem::path& p1,
-               const std::experimental::filesystem::path& p2)
+  compare_paths(const test_fs::path& p1,
+               const test_fs::path& p2)
   {
+    PATH_CHK( p1, p2, native );
     PATH_CHK( p1, p2, string );
     PATH_CHK( p1, p2, empty );
     PATH_CHK( p1, p2, has_root_path );
@@ -54,10 +82,15 @@ namespace __gnu_test
     PATH_CHK( p1, p2, is_relative );
     auto d1 = std::distance(p1.begin(), p1.end());
     auto d2 = std::distance(p2.begin(), p2.end());
-    if( d1 != d2 )
-      throw std::experimental::filesystem::filesystem_error(
-         "distance(begin, end)", p1, p2,
+    if (d1 != d2)
+      throw test_fs::filesystem_error(
+         "distance(begin1, end1) != distance(begin2, end2)", p1, p2,
+         std::make_error_code(std::errc::invalid_argument) );
+    if (!std::equal(p1.begin(), p1.end(), p2.begin()))
+      throw test_fs::filesystem_error(
+         "!equal(begin1, end1, begin2)", p1, p2,
          std::make_error_code(std::errc::invalid_argument) );
+
   }
 
   const std::string test_paths[] = {
@@ -65,31 +98,77 @@ namespace __gnu_test
     "a", "a/b", "a/b/", "a/b/c", "a/b/c.d", "a/b/..", "a/b/c.", "a/b/.c"
   };
 
+  test_fs::path
+  root_path()
+  {
+#if defined(__MINGW32__) || defined(__MINGW64__)
+    return L"c:/";
+#else
+    return "/";
+#endif
+  }
+
   // This is NOT supposed to be a secure way to get a unique name!
   // We just need a path that doesn't exist for testing purposes.
-  std::experimental::filesystem::path
-  nonexistent_path()
+  test_fs::path
+  nonexistent_path(std::string file = __builtin_FILE())
   {
-    std::experimental::filesystem::path p;
+    // Include the caller's filename to help identify tests that fail to
+    // clean up the files they create.
+    // Remove .cc extension:
+    if (file.length() > 3 && file.compare(file.length() - 3, 3, ".cc") == 0)
+      file.resize(file.length() - 3);
+    // And directory:
+    auto pos = file.find_last_of("/\\");
+    if (pos != file.npos)
+      file.erase(0, pos+1);
+
+    file.reserve(file.size() + 40);
+    file.insert(0, "filesystem-test.");
+
+    // A counter, starting from a random value, to be included as part
+    // of the filename being returned, and incremented each time
+    // this function is used.  It allows us to ensure that two calls
+    // to this function can never return the same filename, something
+    // testcases do when they need multiple non-existent filenames
+    // for their purposes.
+    static unsigned counter = std::random_device{}();
+    file += '.';
+    file += std::to_string(counter++);
+    file += '.';
+
+    test_fs::path p;
 #if defined(_GNU_SOURCE) || _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L
-    char tmp[] = "filesystem-ts-test.XXXXXX";
-    int fd = ::mkstemp(tmp);
+
+    // Use mkstemp to determine the name of a file which does not exist yet.
+    //
+    // Note that we have seen on some systems (such as RTEMS, for instance)
+    // that mkstemp behaves very predictably, causing it to always try
+    // the same sequence of file names.  In other words, if we call mkstemp
+    // with a pattern, delete the file it created (which is what we do, here),
+    // and call mkstemp with the same pattern again, it returns the same
+    // filename once more.  While most implementations introduce a degree
+    // of randomness, it is not mandated by the standard, and this is why
+    // we also include a counter in the template passed to mkstemp.
+    file += "XXXXXX";
+    int fd = ::mkstemp(&file[0]);
     if (fd == -1)
-      throw std::experimental::filesystem::filesystem_error("mkstemp failed",
+      throw test_fs::filesystem_error("mkstemp failed",
          std::error_code(errno, std::generic_category()));
-    ::unlink(tmp);
+    ::unlink(file.c_str());
     ::close(fd);
-    p = tmp;
+    p = std::move(file);
 #else
-    char buf[64];
-    static int counter;
-#if _GLIBCXX_USE_C99_STDIO
-    std::snprintf(buf, 64,
-#else
-    std::sprintf(buf,
-#endif
-      "filesystem-ts-test.%d.%lu", counter++, (unsigned long) ::getpid());
-    p = buf;
+    if (file.length() > 64)
+      file.resize(64);
+    // The combination of random counter and PID should be unique for a given
+    // run of the testsuite.  N.B. getpid() returns a pointer type on vxworks
+    // in kernel mode.
+    file += std::to_string((unsigned long) ::getpid());
+    p = std::move(file);
+    if (test_fs::exists(p))
+      throw test_fs::filesystem_error("Failed to generate unique pathname", p,
+         std::make_error_code(std::errc::file_exists));
 #endif
     return p;
   }
@@ -97,20 +176,39 @@ namespace __gnu_test
   // RAII helper to remove a file on scope exit.
   struct scoped_file
   {
-    using path_type = std::experimental::filesystem::path;
+    using path_type = test_fs::path;
 
     enum adopt_file_t { adopt_file };
 
     explicit
     scoped_file(const path_type& p = nonexistent_path()) : path(p)
-    { std::ofstream{p.native()}; }
+    { std::ofstream{p.c_str()}; }
 
     scoped_file(path_type p, adopt_file_t) : path(p) { }
 
     ~scoped_file() { if (!path.empty()) remove(path); }
 
+    scoped_file(scoped_file&&) = default;
+    scoped_file& operator=(scoped_file&&) = default;
+
     path_type path;
   };
 
+  inline bool
+  permissions_are_testable(bool print_msg = true)
+  {
+    bool testable = false;
+#if !(defined __MINGW32__ || defined __MINGW64__)
+    if (geteuid() != 0)
+      testable = true;
+    // XXX on Linux the CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH capabilities
+    // can give normal users extra permissions for files and directories.
+    // We ignore that possibility here.
+#endif
+    if (print_msg && !testable)
+      std::puts("Skipping tests that depend on filesystem permissions");
+    return testable;
+  }
+
 } // namespace __gnu_test
 #endif