Blender V5.0
BLI_fileops_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include <fcntl.h>
6
7#include "testing/testing.h"
8
9#include "BLI_fileops.hh"
10#include "BLI_path_utils.hh"
11#include "BLI_string.h"
12#include "BLI_system.h"
13#include "BLI_tempfile.h"
14#include "BLI_threads.h"
15
16#include BLI_SYSTEM_PID_H
17
18namespace blender::tests {
19
20/*
21 * General `BLI_fileops.h` tests.
22 */
23
24class FileOpsTest : public testing::Test {
25 public:
26 /* The base temp directory for all tests using this helper class. Absolute path. */
27 std::string temp_dir;
28
29 void SetUp() override
30 {
31 char temp_dir_c[FILE_MAX];
32 BLI_temp_directory_path_get(temp_dir_c, sizeof(temp_dir_c));
33
34 temp_dir = std::string(temp_dir_c) + SEP_STR + "blender_fileops_test_" +
35 std::to_string(getpid());
36 if (!BLI_exists(temp_dir.c_str())) {
38 }
39 }
40
41 void TearDown() override
42 {
43 if (BLI_exists(temp_dir.c_str())) {
44 BLI_delete(temp_dir.c_str(), true, true);
45 }
46 }
47};
48
50{
51 const std::string file_name_src = "test_file_src.txt";
52 const std::string file_name_dst = "test_file_dst.txt";
53
54 const std::string test_filepath_src = temp_dir + SEP_STR + file_name_src;
55 const std::string test_filepath_dst = temp_dir + SEP_STR + file_name_dst;
56
57 ASSERT_FALSE(BLI_exists(test_filepath_src.c_str()));
58 ASSERT_FALSE(BLI_exists(test_filepath_dst.c_str()));
59 BLI_file_touch(test_filepath_src.c_str());
60 ASSERT_TRUE(BLI_exists(test_filepath_src.c_str()));
61
62 /* `test_filepath_dst` does not exist, so regular rename should succeed. */
63 ASSERT_EQ(0, BLI_rename(test_filepath_src.c_str(), test_filepath_dst.c_str()));
64 ASSERT_FALSE(BLI_exists(test_filepath_src.c_str()));
65 ASSERT_TRUE(BLI_exists(test_filepath_dst.c_str()));
66
67 BLI_file_touch(test_filepath_src.c_str());
68 ASSERT_TRUE(BLI_exists(test_filepath_src.c_str()));
69
70 /* `test_filepath_dst` does exist now, so regular rename should fail. */
71 ASSERT_NE(0, BLI_rename(test_filepath_src.c_str(), test_filepath_dst.c_str()));
72 ASSERT_TRUE(BLI_exists(test_filepath_src.c_str()));
73 ASSERT_TRUE(BLI_exists(test_filepath_dst.c_str()));
74
75 BLI_file_touch(test_filepath_src.c_str());
76 ASSERT_TRUE(BLI_exists(test_filepath_src.c_str()));
77
78 /* `test_filepath_dst` does exist now, but overwrite rename should succeed on all systems. */
79 ASSERT_EQ(0, BLI_rename_overwrite(test_filepath_src.c_str(), test_filepath_dst.c_str()));
80 ASSERT_FALSE(BLI_exists(test_filepath_src.c_str()));
81 ASSERT_TRUE(BLI_exists(test_filepath_dst.c_str()));
82
83 BLI_file_touch(test_filepath_src.c_str());
84 ASSERT_TRUE(BLI_exists(test_filepath_src.c_str()));
85
86 /* Keep `test_filepath_dst` read-open before attempting to rename `test_filepath_src` to
87 * `test_filepath_dst`.
88 *
89 * This is expected to succeed on Unix, but fail on Windows. */
90 int fd_dst = BLI_open(test_filepath_dst.c_str(), O_BINARY | O_RDONLY, 0);
91#ifdef WIN32
92 ASSERT_NE(0, BLI_rename_overwrite(test_filepath_src.c_str(), test_filepath_dst.c_str()));
93 ASSERT_TRUE(BLI_exists(test_filepath_src.c_str()));
94#else
95 ASSERT_EQ(0, BLI_rename_overwrite(test_filepath_src.c_str(), test_filepath_dst.c_str()));
96 ASSERT_FALSE(BLI_exists(test_filepath_src.c_str()));
97#endif
98 ASSERT_TRUE(BLI_exists(test_filepath_dst.c_str()));
99
100 close(fd_dst);
101
102 /*
103 * Check directory renaming.
104 */
105
106 const std::string dir_name_src = "test_dir_src";
107 const std::string dir_name_dst = "test_dir_dst";
108
109 const std::string test_dirpath_src = temp_dir + SEP_STR + dir_name_src;
110 const std::string test_dirpath_dst = temp_dir + SEP_STR + dir_name_dst;
111
112 BLI_dir_create_recursive(test_dirpath_src.c_str());
113 ASSERT_TRUE(BLI_exists(test_dirpath_src.c_str()));
114
115 /* `test_dirpath_dst` does not exist, so regular rename should succeed. */
116 ASSERT_EQ(0, BLI_rename(test_dirpath_src.c_str(), test_dirpath_dst.c_str()));
117 ASSERT_FALSE(BLI_exists(test_dirpath_src.c_str()));
118 ASSERT_TRUE(BLI_exists(test_dirpath_dst.c_str()));
119
120 BLI_dir_create_recursive(test_dirpath_src.c_str());
121 ASSERT_TRUE(BLI_exists(test_dirpath_src.c_str()));
122
123 /* `test_dirpath_dst` now exists, so regular rename should fail. */
124 ASSERT_NE(0, BLI_rename(test_dirpath_src.c_str(), test_dirpath_dst.c_str()));
125 ASSERT_TRUE(BLI_exists(test_dirpath_src.c_str()));
126 ASSERT_TRUE(BLI_exists(test_dirpath_dst.c_str()));
127
128 /* `test_dirpath_dst` now exists, but is empty, so overwrite rename should succeed. */
129 ASSERT_EQ(0, BLI_rename_overwrite(test_dirpath_src.c_str(), test_dirpath_dst.c_str()));
130 ASSERT_FALSE(BLI_exists(test_dirpath_src.c_str()));
131 ASSERT_TRUE(BLI_exists(test_dirpath_dst.c_str()));
132
133 BLI_dir_create_recursive(test_dirpath_src.c_str());
134 ASSERT_TRUE(BLI_exists(test_dirpath_src.c_str()));
135
136 const std::string test_dir_filepath_src = test_dirpath_src + SEP_STR + file_name_src;
137 const std::string test_dir_filepath_dst = test_dirpath_dst + SEP_STR + file_name_dst;
138
139 ASSERT_FALSE(BLI_exists(test_dir_filepath_src.c_str()));
140 ASSERT_FALSE(BLI_exists(test_dir_filepath_dst.c_str()));
141 BLI_file_touch(test_dir_filepath_src.c_str());
142 ASSERT_TRUE(BLI_exists(test_dir_filepath_src.c_str()));
143
144 /* `test_dir_filepath_src` does not exist, so regular rename should succeed. */
145 ASSERT_EQ(0, BLI_rename(test_dir_filepath_src.c_str(), test_dir_filepath_dst.c_str()));
146 ASSERT_FALSE(BLI_exists(test_dir_filepath_src.c_str()));
147 ASSERT_TRUE(BLI_exists(test_dir_filepath_dst.c_str()));
148
149 /* `test_dirpath_dst` exists and is not empty, so regular rename should fail. */
150 ASSERT_NE(0, BLI_rename(test_dirpath_src.c_str(), test_dirpath_dst.c_str()));
151 ASSERT_TRUE(BLI_exists(test_dirpath_src.c_str()));
152 ASSERT_TRUE(BLI_exists(test_dirpath_dst.c_str()));
153
154 /* `test_dirpath_dst` exists and is not empty, so even overwrite rename should fail. */
155 ASSERT_NE(0, BLI_rename_overwrite(test_dirpath_src.c_str(), test_dirpath_dst.c_str()));
156 ASSERT_TRUE(BLI_exists(test_dirpath_src.c_str()));
157 ASSERT_TRUE(BLI_exists(test_dirpath_dst.c_str()));
158}
159
161{
162 const std::string dir_path = this->temp_dir + SEP_STR + "dir-to-create";
163 const std::string subdir_path = dir_path + SEP_STR + "subdir";
164
165 ASSERT_FALSE(BLI_exists(dir_path.c_str()));
166 ASSERT_TRUE(BLI_dir_create_recursive(subdir_path.c_str()));
167 ASSERT_TRUE(BLI_exists(subdir_path.c_str()));
168
169 ASSERT_TRUE(BLI_dir_create_recursive(subdir_path.c_str()))
170 << "Creating an already-existing directory should be fine";
171
172 const std::string subfile_path = dir_path + SEP_STR + "some_file.txt";
173 ASSERT_TRUE(BLI_file_touch(subfile_path.c_str()));
174 ASSERT_FALSE(BLI_dir_create_recursive(subfile_path.c_str()))
175 << "Creating a directory that already exists as file should return an error status";
176}
177
178/*
179 * blender::fstream tests.
180 */
181
182TEST(fileops, fstream_open_string_filename)
183{
184 const std::string test_files_dir = blender::tests::flags_test_asset_dir();
185 if (test_files_dir.empty()) {
186 FAIL();
187 }
188
189 const std::string filepath = test_files_dir + "/asset_library/новый/blender_assets.cats.txt";
190 fstream in(filepath, std::ios_base::in);
191 ASSERT_TRUE(in.is_open()) << "could not open " << filepath;
192 in.close(); /* This should not crash. */
193
194 /* Reading the file not tested here. That's deferred to `std::fstream` anyway. */
195}
196
197TEST(fileops, fstream_open_charptr_filename)
198{
199 const std::string test_files_dir = blender::tests::flags_test_asset_dir();
200 if (test_files_dir.empty()) {
201 FAIL();
202 }
203
204 const std::string filepath_str = test_files_dir + "/asset_library/новый/blender_assets.cats.txt";
205 const char *filepath = filepath_str.c_str();
206 fstream in(filepath, std::ios_base::in);
207 ASSERT_TRUE(in.is_open()) << "could not open " << filepath;
208 in.close(); /* This should not crash. */
209
210 /* Reading the file not tested here. That's deferred to `std::fstream` anyway. */
211}
212
213/*
214 * Current Directory operations tests.
215 */
216
217class ChangeWorkingDirectoryTest : public testing::Test {
218 public:
219 std::string test_temp_dir;
220
221 void SetUp() override
222 {
223 /* Must use because BLI_change_working_dir() checks that we are on the main thread. */
225 }
226
227 void TearDown() override
228 {
229 if (!test_temp_dir.empty()) {
230 BLI_delete(test_temp_dir.c_str(), true, false);
231 }
232
234 }
235
236 /* Make a pseudo-unique file name file within the temp directory in a cross-platform manner. */
238 {
239 char temp_dir[FILE_MAX];
240 BLI_temp_directory_path_get(temp_dir, sizeof(temp_dir));
241
242 const std::string directory_name = "blender_test_" + std::to_string(getpid());
243
244 char filepath[FILE_MAX];
245 BLI_path_join(filepath, sizeof(filepath), temp_dir, directory_name.c_str());
246
247 return filepath;
248 }
249};
250
251TEST_F(ChangeWorkingDirectoryTest, change_working_directory)
252{
253 char original_cwd_buff[FILE_MAX];
254 char *original_cwd = BLI_current_working_dir(original_cwd_buff, sizeof(original_cwd_buff));
255
256 ASSERT_FALSE(original_cwd == nullptr) << "Unable to get the current working directory.";
257 /* While some implementation of `getcwd` (or similar) may return allocated memory in some cases,
258 * in the context of `BLI_current_working_dir` usages, this is not expected and should not
259 * happen. */
260 ASSERT_TRUE(original_cwd == original_cwd_buff)
261 << "Returned CWD path unexpectedly different than given char buffer.";
262
263 std::string temp_file_name = make_pseudo_unique_temp_filename();
264 test_temp_dir = temp_file_name + "_новый";
265
266 if (BLI_exists(test_temp_dir.c_str())) {
267 BLI_delete(test_temp_dir.c_str(), true, false);
268 }
269
270 ASSERT_FALSE(BLI_change_working_dir(test_temp_dir.c_str()))
271 << "changing directory to a non-existent directory is expected to fail.";
272
273 ASSERT_TRUE(BLI_dir_create_recursive(test_temp_dir.c_str()))
274 << "temporary directory should have been created successfully.";
275
276 ASSERT_TRUE(BLI_change_working_dir(test_temp_dir.c_str()))
277 << "temporary directory should succeed changing directory.";
278
279 char new_cwd_buff[FILE_MAX];
280 char *new_cwd = BLI_current_working_dir(new_cwd_buff, sizeof(new_cwd_buff));
281 ASSERT_FALSE(new_cwd == nullptr) << "Unable to get the current working directory.";
282 ASSERT_TRUE(new_cwd == new_cwd_buff)
283 << "Returned CWD path unexpectedly different than given char buffer.";
284
285#ifdef __APPLE__
286 /* The name returned by `std::tmpnam` is fine but the Apple OS method
287 * picks the true var folder, not the alias, meaning the below
288 * comparison always fails unless we prepend with the correct value. */
289 test_temp_dir = "/private" + test_temp_dir;
290#endif // #ifdef __APPLE__
291
292 ASSERT_EQ(BLI_path_cmp_normalized(new_cwd, test_temp_dir.c_str()), 0)
293 << "the path of the current working directory should equal the path of the temporary "
294 "directory that was created.";
295
296 ASSERT_TRUE(BLI_change_working_dir(original_cwd))
297 << "changing directory back to the original working directory should succeed.";
298
299 char final_cwd_buff[FILE_MAX];
300 char *final_cwd = BLI_current_working_dir(final_cwd_buff, sizeof(final_cwd_buff));
301 ASSERT_FALSE(final_cwd == nullptr) << "Unable to get the current working directory.";
302 ASSERT_TRUE(final_cwd == final_cwd_buff)
303 << "Returned CWD path unexpectedly different than given char buffer.";
304
305 ASSERT_EQ(BLI_path_cmp_normalized(final_cwd, original_cwd), 0)
306 << "The final CWD path should be the same as the original CWD path.";
307}
308
309} // namespace blender::tests
bool BLI_change_working_dir(const char *dir)
Definition storage.cc:63
bool BLI_file_touch(const char *filepath) ATTR_NONNULL(1)
Definition fileops_c.cc:316
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:360
#define O_BINARY
bool BLI_dir_create_recursive(const char *dirname) ATTR_NONNULL()
Definition fileops_c.cc:414
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
int BLI_rename(const char *from, const char *to) ATTR_NONNULL()
Definition fileops_c.cc:461
char * BLI_current_working_dir(char *dir, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:81
int BLI_rename_overwrite(const char *from, const char *to) ATTR_NONNULL()
Definition fileops_c.cc:528
int BLI_open(const char *filepath, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
File and directory operations.
#define FILE_MAX
#define BLI_path_join(...)
int BLI_path_cmp_normalized(const char *p1, const char *p2) ATTR_NONNULL(1
bool void BLI_temp_directory_path_get(char *tempdir, const size_t tempdir_maxncpy) ATTR_NONNULL(1)
Definition tempfile.cc:57
void BLI_threadapi_init(void)
Definition threads.cc:114
void BLI_threadapi_exit(void)
Definition threads.cc:119
static bool dir_create_recursive(const char *dirname, const int len)
Definition fileops_c.cc:354
#define in
TEST_F(FileOpsTest, rename)
TEST(blf_load, load)
Definition BLF_tests.cc:34
#define SEP_STR
Definition unit.cc:39