]> git.ipfire.org Git - thirdparty/gcc.git/blob - libstdc++-v3/testsuite/util/testsuite_fs.h
libstdc++: testsuite: skip fs space tests on dummy implementations
[thirdparty/gcc.git] / libstdc++-v3 / testsuite / util / testsuite_fs.h
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
22 #ifndef _TESTSUITE_FS_H
23 #define _TESTSUITE_FS_H 1
24
25 // Assume we want std::filesystem in C++17, unless USE_FILESYSTEM_TS defined:
26 #if __cplusplus >= 201703L && ! defined USE_FILESYSTEM_TS
27 #include <filesystem>
28 namespace test_fs = std::filesystem;
29 #else
30 #include <experimental/filesystem>
31 namespace test_fs = std::experimental::filesystem;
32 #endif
33 #include <algorithm>
34 #include <fstream>
35 #include <string>
36 #include <cstdio>
37 #include <unistd.h> // unlink, close, getpid, geteuid
38
39 #if defined(_GNU_SOURCE) || _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L
40 #include <stdlib.h> // mkstemp
41 #else
42 #include <random> // std::random_device
43 #endif
44
45 #ifndef _GLIBCXX_HAVE_SYMLINK
46 #define NO_SYMLINKS
47 #endif
48
49 #if !defined (_GLIBCXX_HAVE_SYS_STATVFS_H) \
50 && !defined (_GLIBCXX_FILESYSTEM_IS_WINDOWS)
51 #define NO_SPACE
52 #endif
53
54 namespace __gnu_test
55 {
56 #define PATH_CHK(p1, p2, fn) \
57 if ( p1.fn() != p2.fn() ) \
58 throw test_fs::filesystem_error("comparing '" #fn "' failed", p1, p2, \
59 std::make_error_code(std::errc::invalid_argument) )
60
61 void
62 compare_paths(const test_fs::path& p1,
63 const test_fs::path& p2)
64 {
65 PATH_CHK( p1, p2, native );
66 PATH_CHK( p1, p2, string );
67 PATH_CHK( p1, p2, empty );
68 PATH_CHK( p1, p2, has_root_path );
69 PATH_CHK( p1, p2, has_root_name );
70 PATH_CHK( p1, p2, has_root_directory );
71 PATH_CHK( p1, p2, has_relative_path );
72 PATH_CHK( p1, p2, has_parent_path );
73 PATH_CHK( p1, p2, has_filename );
74 PATH_CHK( p1, p2, has_stem );
75 PATH_CHK( p1, p2, has_extension );
76 PATH_CHK( p1, p2, is_absolute );
77 PATH_CHK( p1, p2, is_relative );
78 auto d1 = std::distance(p1.begin(), p1.end());
79 auto d2 = std::distance(p2.begin(), p2.end());
80 if (d1 != d2)
81 throw test_fs::filesystem_error(
82 "distance(begin1, end1) != distance(begin2, end2)", p1, p2,
83 std::make_error_code(std::errc::invalid_argument) );
84 if (!std::equal(p1.begin(), p1.end(), p2.begin()))
85 throw test_fs::filesystem_error(
86 "!equal(begin1, end1, begin2)", p1, p2,
87 std::make_error_code(std::errc::invalid_argument) );
88
89 }
90
91 const std::string test_paths[] = {
92 "", "/", "//", "/.", "/./", "/a", "/a/", "/a//", "/a/b/c/d", "/a//b",
93 "a", "a/b", "a/b/", "a/b/c", "a/b/c.d", "a/b/..", "a/b/c.", "a/b/.c"
94 };
95
96 test_fs::path
97 root_path()
98 {
99 #if defined(__MINGW32__) || defined(__MINGW64__)
100 return L"c:/";
101 #else
102 return "/";
103 #endif
104 }
105
106 // This is NOT supposed to be a secure way to get a unique name!
107 // We just need a path that doesn't exist for testing purposes.
108 test_fs::path
109 nonexistent_path(std::string file = __builtin_FILE())
110 {
111 // Include the caller's filename to help identify tests that fail to
112 // clean up the files they create.
113 // Remove .cc extension:
114 if (file.length() > 3 && file.compare(file.length() - 3, 3, ".cc") == 0)
115 file.resize(file.length() - 3);
116 // And directory:
117 auto pos = file.find_last_of("/\\");
118 if (pos != file.npos)
119 file.erase(0, pos+1);
120
121 test_fs::path p;
122 #if defined(_GNU_SOURCE) || _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L
123 char tmp[] = "filesystem-test.XXXXXX";
124 int fd = ::mkstemp(tmp);
125 if (fd == -1)
126 throw test_fs::filesystem_error("mkstemp failed",
127 std::error_code(errno, std::generic_category()));
128 ::unlink(tmp);
129 ::close(fd);
130 if (!file.empty())
131 file.insert(0, 1, '-');
132 file.insert(0, tmp);
133 p = file;
134 #else
135 if (file.length() > 64)
136 file.resize(64);
137 char buf[128];
138 static unsigned counter = std::random_device{}();
139 #if _GLIBCXX_USE_C99_STDIO
140 std::snprintf(buf, 128,
141 #else
142 std::sprintf(buf,
143 #endif
144 "filesystem-test.%u.%lu-%s", counter++, (unsigned long) ::getpid(),
145 file.c_str());
146 p = buf;
147 #endif
148 return p;
149 }
150
151 // RAII helper to remove a file on scope exit.
152 struct scoped_file
153 {
154 using path_type = test_fs::path;
155
156 enum adopt_file_t { adopt_file };
157
158 explicit
159 scoped_file(const path_type& p = nonexistent_path()) : path(p)
160 { std::ofstream{p.c_str()}; }
161
162 scoped_file(path_type p, adopt_file_t) : path(p) { }
163
164 ~scoped_file() { if (!path.empty()) remove(path); }
165
166 scoped_file(scoped_file&&) = default;
167 scoped_file& operator=(scoped_file&&) = default;
168
169 path_type path;
170 };
171
172 inline bool
173 permissions_are_testable(bool print_msg = true)
174 {
175 bool testable = false;
176 #if !(defined __MINGW32__ || defined __MINGW64__)
177 if (geteuid() != 0)
178 testable = true;
179 // XXX on Linux the CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH capabilities
180 // can give normal users extra permissions for files and directories.
181 // We ignore that possibility here.
182 #endif
183 if (print_msg && !testable)
184 std::puts("Skipping tests that depend on filesystem permissions");
185 return testable;
186 }
187
188 } // namespace __gnu_test
189 #endif