Blender V4.3
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
160/*
161 * blender::fstream tests.
162 */
163
164TEST(fileops, fstream_open_string_filename)
165{
166 const std::string test_files_dir = blender::tests::flags_test_asset_dir();
167 if (test_files_dir.empty()) {
168 FAIL();
169 }
170
171 const std::string filepath = test_files_dir + "/asset_library/новый/blender_assets.cats.txt";
172 fstream in(filepath, std::ios_base::in);
173 ASSERT_TRUE(in.is_open()) << "could not open " << filepath;
174 in.close(); /* This should not crash. */
175
176 /* Reading the file not tested here. That's deferred to `std::fstream` anyway. */
177}
178
179TEST(fileops, fstream_open_charptr_filename)
180{
181 const std::string test_files_dir = blender::tests::flags_test_asset_dir();
182 if (test_files_dir.empty()) {
183 FAIL();
184 }
185
186 const std::string filepath_str = test_files_dir + "/asset_library/новый/blender_assets.cats.txt";
187 const char *filepath = filepath_str.c_str();
188 fstream in(filepath, std::ios_base::in);
189 ASSERT_TRUE(in.is_open()) << "could not open " << filepath;
190 in.close(); /* This should not crash. */
191
192 /* Reading the file not tested here. That's deferred to `std::fstream` anyway. */
193}
194
195/*
196 * Current Directory operations tests.
197 */
198
199class ChangeWorkingDirectoryTest : public testing::Test {
200 public:
201 std::string test_temp_dir;
202
203 void SetUp() override
204 {
205 /* Must use because BLI_change_working_dir() checks that we are on the main thread. */
207 }
208
209 void TearDown() override
210 {
211 if (!test_temp_dir.empty()) {
212 BLI_delete(test_temp_dir.c_str(), true, false);
213 }
214
216 }
217
218 /* Make a pseudo-unique file name file within the temp directory in a cross-platform manner. */
220 {
221 char temp_dir[FILE_MAX];
222 BLI_temp_directory_path_get(temp_dir, sizeof(temp_dir));
223
224 const std::string directory_name = "blender_test_" + std::to_string(getpid());
225
226 char filepath[FILE_MAX];
227 BLI_path_join(filepath, sizeof(filepath), temp_dir, directory_name.c_str());
228
229 return filepath;
230 }
231};
232
233TEST_F(ChangeWorkingDirectoryTest, change_working_directory)
234{
235 char original_cwd_buff[FILE_MAX];
236 char *original_cwd = BLI_current_working_dir(original_cwd_buff, sizeof(original_cwd_buff));
237
238 ASSERT_FALSE(original_cwd == nullptr) << "Unable to get the current working directory.";
239 /* While some implementation of `getcwd` (or similar) may return allocated memory in some cases,
240 * in the context of `BLI_current_working_dir` usages, this is not expected and should not
241 * happen. */
242 ASSERT_TRUE(original_cwd == original_cwd_buff)
243 << "Returned CWD path unexpectedly different than given char buffer.";
244
245 std::string temp_file_name = make_pseudo_unique_temp_filename();
246 test_temp_dir = temp_file_name + "_новый";
247
248 if (BLI_exists(test_temp_dir.c_str())) {
249 BLI_delete(test_temp_dir.c_str(), true, false);
250 }
251
252 ASSERT_FALSE(BLI_change_working_dir(test_temp_dir.c_str()))
253 << "changing directory to a non-existent directory is expected to fail.";
254
255 ASSERT_TRUE(BLI_dir_create_recursive(test_temp_dir.c_str()))
256 << "temporary directory should have been created successfully.";
257
258 ASSERT_TRUE(BLI_change_working_dir(test_temp_dir.c_str()))
259 << "temporary directory should succeed changing directory.";
260
261 char new_cwd_buff[FILE_MAX];
262 char *new_cwd = BLI_current_working_dir(new_cwd_buff, sizeof(new_cwd_buff));
263 ASSERT_FALSE(new_cwd == nullptr) << "Unable to get the current working directory.";
264 ASSERT_TRUE(new_cwd == new_cwd_buff)
265 << "Returned CWD path unexpectedly different than given char buffer.";
266
267#ifdef __APPLE__
268 /* The name returned by `std::tmpnam` is fine but the Apple OS method
269 * picks the true var folder, not the alias, meaning the below
270 * comparison always fails unless we prepend with the correct value. */
271 test_temp_dir = "/private" + test_temp_dir;
272#endif // #ifdef __APPLE__
273
274 ASSERT_EQ(BLI_path_cmp_normalized(new_cwd, test_temp_dir.c_str()), 0)
275 << "the path of the current working directory should equal the path of the temporary "
276 "directory that was created.";
277
278 ASSERT_TRUE(BLI_change_working_dir(original_cwd))
279 << "changing directory back to the original working directory should succeed.";
280
281 char final_cwd_buff[FILE_MAX];
282 char *final_cwd = BLI_current_working_dir(final_cwd_buff, sizeof(final_cwd_buff));
283 ASSERT_FALSE(final_cwd == nullptr) << "Unable to get the current working directory.";
284 ASSERT_TRUE(final_cwd == final_cwd_buff)
285 << "Returned CWD path unexpectedly different than given char buffer.";
286
287 ASSERT_EQ(BLI_path_cmp_normalized(final_cwd, original_cwd), 0)
288 << "The final CWD path should be the same as the original CWD path.";
289}
290
291} // 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:350
#define O_BINARY
bool BLI_dir_create_recursive(const char *dirname) ATTR_NONNULL()
Definition fileops_c.cc:391
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:438
char * BLI_current_working_dir(char *dir, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:85
int BLI_rename_overwrite(const char *from, const char *to) ATTR_NONNULL()
Definition fileops_c.cc:505
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:53
void BLI_threadapi_init(void)
Definition threads.cc:114
void BLI_threadapi_exit(void)
Definition threads.cc:119
TEST_F(FileOpsTest, rename)
TEST(any, DefaultConstructor)
#define SEP_STR
Definition unit.cc:39