1#include "test.h"
2
3#include <base/system.h>
4
5#include <gtest/gtest.h>
6
7TEST(Filesystem, Filename)
8{
9 EXPECT_STREQ(fs_filename(""), "");
10 EXPECT_STREQ(fs_filename("a"), "a");
11 EXPECT_STREQ(fs_filename("abc"), "abc");
12 EXPECT_STREQ(fs_filename("a/b"), "b");
13 EXPECT_STREQ(fs_filename("a/b/c"), "c");
14 EXPECT_STREQ(fs_filename("aaaaa/bbbb/ccc"), "ccc");
15 EXPECT_STREQ(fs_filename("aaaaa\\bbbb\\ccc"), "ccc");
16 EXPECT_STREQ(fs_filename("aaaaa/bbbb\\ccc"), "ccc");
17 EXPECT_STREQ(fs_filename("aaaaa\\bbbb/ccc"), "ccc");
18}
19
20TEST(Filesystem, SplitFileExtension)
21{
22 char aName[IO_MAX_PATH_LENGTH];
23 char aExt[IO_MAX_PATH_LENGTH];
24
25 fs_split_file_extension(filename: "", name: aName, name_size: sizeof(aName), extension: aExt, extension_size: sizeof(aExt));
26 EXPECT_STREQ(aName, "");
27 EXPECT_STREQ(aExt, "");
28
29 fs_split_file_extension(filename: "name.ext", name: aName, name_size: sizeof(aName), extension: aExt, extension_size: sizeof(aExt));
30 EXPECT_STREQ(aName, "name");
31 EXPECT_STREQ(aExt, "ext");
32
33 fs_split_file_extension(filename: "name.ext", name: aName, name_size: sizeof(aName)); // extension parameter is optional
34 EXPECT_STREQ(aName, "name");
35
36 fs_split_file_extension(filename: "name.ext", name: nullptr, name_size: 0, extension: aExt, extension_size: sizeof(aExt)); // name parameter is optional
37 EXPECT_STREQ(aExt, "ext");
38
39 fs_split_file_extension(filename: "archive.tar.gz", name: aName, name_size: sizeof(aName), extension: aExt, extension_size: sizeof(aExt));
40 EXPECT_STREQ(aName, "archive.tar");
41 EXPECT_STREQ(aExt, "gz");
42
43 fs_split_file_extension(filename: "no_dot", name: aName, name_size: sizeof(aName), extension: aExt, extension_size: sizeof(aExt));
44 EXPECT_STREQ(aName, "no_dot");
45 EXPECT_STREQ(aExt, "");
46
47 fs_split_file_extension(filename: "no_dot", name: aName, name_size: sizeof(aName)); // extension parameter is optional
48 EXPECT_STREQ(aName, "no_dot");
49
50 fs_split_file_extension(filename: "no_dot", name: nullptr, name_size: 0, extension: aExt, extension_size: sizeof(aExt)); // name parameter is optional
51 EXPECT_STREQ(aExt, "");
52
53 fs_split_file_extension(filename: ".dot_first", name: aName, name_size: sizeof(aName), extension: aExt, extension_size: sizeof(aExt));
54 EXPECT_STREQ(aName, ".dot_first");
55 EXPECT_STREQ(aExt, "");
56
57 fs_split_file_extension(filename: ".dot_first", name: aName, name_size: sizeof(aName)); // extension parameter is optional
58 EXPECT_STREQ(aName, ".dot_first");
59
60 fs_split_file_extension(filename: ".dot_first", name: nullptr, name_size: 0, extension: aExt, extension_size: sizeof(aExt)); // name parameter is optional
61 EXPECT_STREQ(aExt, "");
62}
63
64static void TestNormalizePath(const char *pInput, const char *pExpectedOutput)
65{
66 char aNormalized[256];
67 str_copy(dst&: aNormalized, src: pInput);
68 fs_normalize_path(path: aNormalized);
69 EXPECT_STREQ(aNormalized, pExpectedOutput);
70}
71
72TEST(Filesystem, NormalizePath)
73{
74 TestNormalizePath(pInput: "", pExpectedOutput: "");
75 TestNormalizePath(pInput: "/", pExpectedOutput: "/");
76 TestNormalizePath(pInput: "\\", pExpectedOutput: "/");
77 TestNormalizePath(pInput: "//", pExpectedOutput: "/");
78 TestNormalizePath(pInput: "/////", pExpectedOutput: "/");
79 TestNormalizePath(pInput: "\\\\\\\\\\", pExpectedOutput: "/");
80 TestNormalizePath(pInput: "/a/b/c", pExpectedOutput: "/a/b/c");
81 TestNormalizePath(pInput: "\\a\\b\\c", pExpectedOutput: "/a/b/c");
82 TestNormalizePath(pInput: "C:\\Users", pExpectedOutput: "C:/Users");
83 TestNormalizePath(pInput: "C:\\", pExpectedOutput: "C:");
84}
85
86TEST(Filesystem, StoragePath)
87{
88 char aStoragePath[IO_MAX_PATH_LENGTH];
89 ASSERT_FALSE(fs_storage_path("TestAppName", aStoragePath, sizeof(aStoragePath)));
90 EXPECT_FALSE(fs_is_relative_path(aStoragePath));
91 EXPECT_TRUE(str_endswith_nocase(aStoragePath, "/TestAppName"));
92}
93
94TEST(Filesystem, ExecutablePath)
95{
96 char aExecutablePath[IO_MAX_PATH_LENGTH];
97 ASSERT_FALSE(fs_executable_path(aExecutablePath, sizeof(aExecutablePath)));
98 EXPECT_TRUE(fs_is_file(aExecutablePath));
99 fs_parent_dir(path: aExecutablePath);
100 EXPECT_FALSE(fs_is_relative_path(aExecutablePath));
101}
102
103TEST(Filesystem, CreateCloseDelete)
104{
105 CTestInfo Info;
106
107 EXPECT_FALSE(fs_is_file(Info.m_aFilename));
108 IOHANDLE File = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
109 ASSERT_TRUE(File);
110 EXPECT_FALSE(io_close(File));
111 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
112 EXPECT_FALSE(fs_remove(Info.m_aFilename));
113 EXPECT_FALSE(fs_is_file(Info.m_aFilename));
114}
115
116TEST(Filesystem, CreateDeleteDirectory)
117{
118 CTestInfo Info;
119 char aFilename[IO_MAX_PATH_LENGTH];
120 str_format(buffer: aFilename, buffer_size: sizeof(aFilename), format: "%s/test.txt", Info.m_aFilename);
121
122 EXPECT_FALSE(fs_is_dir(Info.m_aFilename));
123 EXPECT_FALSE(fs_makedir(Info.m_aFilename));
124 EXPECT_TRUE(fs_is_dir(Info.m_aFilename));
125
126 IOHANDLE File = io_open(filename: aFilename, flags: IOFLAG_WRITE);
127 ASSERT_TRUE(File);
128 EXPECT_FALSE(io_close(File));
129
130 // Directory removal fails if there are any files left in the directory.
131 EXPECT_TRUE(fs_removedir(Info.m_aFilename));
132 EXPECT_TRUE(fs_is_dir(Info.m_aFilename));
133
134 EXPECT_FALSE(fs_remove(aFilename));
135 EXPECT_FALSE(fs_removedir(Info.m_aFilename));
136 EXPECT_FALSE(fs_is_dir(Info.m_aFilename));
137}
138
139TEST(Filesystem, CantDeleteDirectoryWithRemove)
140{
141 CTestInfo Info;
142 EXPECT_FALSE(fs_makedir(Info.m_aFilename));
143 EXPECT_TRUE(fs_remove(Info.m_aFilename)); // Cannot remove directory with file removal function.
144 EXPECT_FALSE(fs_removedir(Info.m_aFilename));
145}
146
147TEST(Filesystem, CantDeleteFileWithRemoveDirectory)
148{
149 CTestInfo Info;
150 IOHANDLE File = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
151 ASSERT_TRUE(File);
152 EXPECT_FALSE(io_close(File));
153 EXPECT_TRUE(fs_removedir(Info.m_aFilename)); // Cannot remove file with directory removal function.
154 EXPECT_FALSE(fs_remove(Info.m_aFilename));
155}
156
157TEST(Filesystem, DeleteNonexistentFile)
158{
159 CTestInfo Info;
160
161 EXPECT_FALSE(fs_is_file(Info.m_aFilename));
162 EXPECT_FALSE(fs_remove(Info.m_aFilename)); // Can delete a file that does not exist.
163}
164
165TEST(Filesystem, DeleteNonexistentDirectory)
166{
167 CTestInfo Info;
168
169 EXPECT_FALSE(fs_is_dir(Info.m_aFilename));
170 EXPECT_FALSE(fs_removedir(Info.m_aFilename)); // Can delete a folder that does not exist.
171}
172
173TEST(Filesystem, DeleteOpenFile)
174{
175 CTestInfo Info;
176
177 IOHANDLE File = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
178 ASSERT_TRUE(File);
179
180 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
181 EXPECT_FALSE(fs_remove(Info.m_aFilename)); // Can delete a file that has open handle.
182 EXPECT_FALSE(fs_is_file(Info.m_aFilename)); // File should be gone immediately even before the last file handle is closed.
183
184 EXPECT_FALSE(io_close(File));
185}
186
187TEST(Filesystem, DeleteOpenFileMultipleHandles)
188{
189 CTestInfo Info;
190
191 IOHANDLE FileWrite1 = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
192 ASSERT_TRUE(FileWrite1);
193
194 IOHANDLE FileRead1 = io_open(filename: Info.m_aFilename, flags: IOFLAG_READ);
195 ASSERT_TRUE(FileRead1);
196
197 IOHANDLE FileWrite2 = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
198 ASSERT_TRUE(FileWrite2);
199
200 IOHANDLE FileRead2 = io_open(filename: Info.m_aFilename, flags: IOFLAG_READ);
201 ASSERT_TRUE(FileRead2);
202
203 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
204 EXPECT_FALSE(fs_remove(Info.m_aFilename)); // Can delete a file that has multiple open handles.
205 EXPECT_FALSE(fs_is_file(Info.m_aFilename)); // File should be gone immediately even before the last file handle is closed.
206
207 EXPECT_FALSE(io_close(FileWrite1));
208 EXPECT_FALSE(io_close(FileRead1));
209 EXPECT_FALSE(io_close(FileWrite2));
210 EXPECT_FALSE(io_close(FileRead2));
211}
212
213TEST(Filesystem, RenameFile)
214{
215 char aNewFilename[IO_MAX_PATH_LENGTH];
216 CTestInfo Info;
217 Info.Filename(pBuffer: aNewFilename, BufferLength: sizeof(aNewFilename), pSuffix: ".renamed");
218
219 IOHANDLE FileWrite = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
220 ASSERT_TRUE(FileWrite);
221 EXPECT_FALSE(io_close(FileWrite));
222
223 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
224 EXPECT_FALSE(fs_rename(Info.m_aFilename, aNewFilename));
225 EXPECT_FALSE(fs_is_file(Info.m_aFilename));
226 EXPECT_TRUE(fs_is_file(aNewFilename));
227
228 EXPECT_FALSE(fs_remove(aNewFilename));
229}
230
231TEST(Filesystem, RenameFolder)
232{
233 char aNewFilename[IO_MAX_PATH_LENGTH];
234 CTestInfo Info;
235 Info.Filename(pBuffer: aNewFilename, BufferLength: sizeof(aNewFilename), pSuffix: ".renamed");
236
237 EXPECT_FALSE(fs_makedir(Info.m_aFilename));
238
239 EXPECT_TRUE(fs_is_dir(Info.m_aFilename));
240 EXPECT_FALSE(fs_rename(Info.m_aFilename, aNewFilename));
241 EXPECT_FALSE(fs_is_dir(Info.m_aFilename));
242 EXPECT_TRUE(fs_is_dir(aNewFilename));
243
244 EXPECT_FALSE(fs_removedir(aNewFilename));
245}
246
247TEST(Filesystem, RenameOpenFileSource)
248{
249 char aNewFilename[IO_MAX_PATH_LENGTH];
250 CTestInfo Info;
251 Info.Filename(pBuffer: aNewFilename, BufferLength: sizeof(aNewFilename), pSuffix: ".renamed");
252
253 IOHANDLE FileWrite = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
254 ASSERT_TRUE(FileWrite);
255
256 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
257 EXPECT_FALSE(fs_rename(Info.m_aFilename, aNewFilename)); // Can rename a file that has open handle.
258 EXPECT_FALSE(fs_is_file(Info.m_aFilename)); // Rename should take effect immediately.
259 EXPECT_TRUE(fs_is_file(aNewFilename));
260
261 EXPECT_FALSE(io_close(FileWrite));
262
263 EXPECT_FALSE(fs_remove(aNewFilename));
264}
265
266TEST(Filesystem, RenameOpenFileSourceMultipleHandles)
267{
268 char aNewFilename[IO_MAX_PATH_LENGTH];
269 CTestInfo Info;
270 Info.Filename(pBuffer: aNewFilename, BufferLength: sizeof(aNewFilename), pSuffix: ".renamed");
271
272 IOHANDLE FileWrite1 = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
273 ASSERT_TRUE(FileWrite1);
274
275 IOHANDLE FileRead1 = io_open(filename: Info.m_aFilename, flags: IOFLAG_READ);
276 ASSERT_TRUE(FileRead1);
277
278 IOHANDLE FileWrite2 = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
279 ASSERT_TRUE(FileWrite2);
280
281 IOHANDLE FileRead2 = io_open(filename: Info.m_aFilename, flags: IOFLAG_READ);
282 ASSERT_TRUE(FileRead2);
283
284 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
285 EXPECT_FALSE(fs_rename(Info.m_aFilename, aNewFilename)); // Can rename a file that has multiple open handles.
286 EXPECT_FALSE(fs_is_file(Info.m_aFilename)); // Rename should take effect immediately.
287 EXPECT_TRUE(fs_is_file(aNewFilename));
288
289 EXPECT_FALSE(io_close(FileWrite1));
290 EXPECT_FALSE(io_close(FileRead1));
291 EXPECT_FALSE(io_close(FileWrite2));
292 EXPECT_FALSE(io_close(FileRead2));
293
294 EXPECT_FALSE(fs_remove(aNewFilename));
295}
296
297TEST(Filesystem, RenameTargetFileExists)
298{
299 char aNewFilename[IO_MAX_PATH_LENGTH];
300 CTestInfo Info;
301 Info.Filename(pBuffer: aNewFilename, BufferLength: sizeof(aNewFilename), pSuffix: ".renamed");
302
303 IOHANDLE FileWrite1 = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
304 ASSERT_TRUE(FileWrite1);
305 EXPECT_FALSE(io_close(FileWrite1));
306
307 IOHANDLE FileWrite2 = io_open(filename: aNewFilename, flags: IOFLAG_WRITE);
308 ASSERT_TRUE(FileWrite2);
309 EXPECT_FALSE(io_close(FileWrite2));
310
311 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
312 EXPECT_TRUE(fs_is_file(aNewFilename));
313 EXPECT_FALSE(fs_rename(Info.m_aFilename, aNewFilename)); // Renaming can overwrite the existing target file if it has no open handles.
314 EXPECT_FALSE(fs_is_file(Info.m_aFilename));
315 EXPECT_TRUE(fs_is_file(aNewFilename));
316
317 EXPECT_FALSE(fs_remove(aNewFilename));
318}
319
320TEST(Filesystem, RenameOpenFileDeleteTarget)
321{
322 char aNewFilename[IO_MAX_PATH_LENGTH];
323 CTestInfo Info;
324 Info.Filename(pBuffer: aNewFilename, BufferLength: sizeof(aNewFilename), pSuffix: ".renamed");
325
326 IOHANDLE FileWrite1 = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
327 ASSERT_TRUE(FileWrite1);
328 EXPECT_FALSE(io_close(FileWrite1));
329
330 IOHANDLE FileWrite2 = io_open(filename: aNewFilename, flags: IOFLAG_WRITE);
331 ASSERT_TRUE(FileWrite2);
332 EXPECT_FALSE(io_close(FileWrite2));
333
334 IOHANDLE FileRead = io_open(filename: aNewFilename, flags: IOFLAG_READ);
335 ASSERT_TRUE(FileRead);
336
337 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
338 EXPECT_TRUE(fs_is_file(aNewFilename));
339 EXPECT_FALSE(fs_remove(aNewFilename)); // Target file must be deleted else rename fails on Windows when target file has open handle.
340 EXPECT_FALSE(fs_rename(Info.m_aFilename, aNewFilename));
341 EXPECT_FALSE(fs_is_file(Info.m_aFilename));
342 EXPECT_TRUE(fs_is_file(aNewFilename));
343
344 EXPECT_FALSE(io_close(FileRead));
345
346 EXPECT_FALSE(fs_remove(aNewFilename));
347}
348