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, CreateCloseDelete)
95{
96 CTestInfo Info;
97
98 EXPECT_FALSE(fs_is_file(Info.m_aFilename));
99 IOHANDLE File = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
100 ASSERT_TRUE(File);
101 EXPECT_FALSE(io_close(File));
102 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
103 EXPECT_FALSE(fs_remove(Info.m_aFilename));
104 EXPECT_FALSE(fs_is_file(Info.m_aFilename));
105}
106
107TEST(Filesystem, CreateDeleteDirectory)
108{
109 CTestInfo Info;
110 char aFilename[IO_MAX_PATH_LENGTH];
111 str_format(buffer: aFilename, buffer_size: sizeof(aFilename), format: "%s/test.txt", Info.m_aFilename);
112
113 EXPECT_FALSE(fs_is_dir(Info.m_aFilename));
114 EXPECT_FALSE(fs_makedir(Info.m_aFilename));
115 EXPECT_TRUE(fs_is_dir(Info.m_aFilename));
116
117 IOHANDLE File = io_open(filename: aFilename, flags: IOFLAG_WRITE);
118 ASSERT_TRUE(File);
119 EXPECT_FALSE(io_close(File));
120
121 // Directory removal fails if there are any files left in the directory.
122 EXPECT_TRUE(fs_removedir(Info.m_aFilename));
123 EXPECT_TRUE(fs_is_dir(Info.m_aFilename));
124
125 EXPECT_FALSE(fs_remove(aFilename));
126 EXPECT_FALSE(fs_removedir(Info.m_aFilename));
127 EXPECT_FALSE(fs_is_dir(Info.m_aFilename));
128}
129
130TEST(Filesystem, CantDeleteDirectoryWithRemove)
131{
132 CTestInfo Info;
133 EXPECT_FALSE(fs_makedir(Info.m_aFilename));
134 EXPECT_TRUE(fs_remove(Info.m_aFilename)); // Cannot remove directory with file removal function.
135 EXPECT_FALSE(fs_removedir(Info.m_aFilename));
136}
137
138TEST(Filesystem, CantDeleteFileWithRemoveDirectory)
139{
140 CTestInfo Info;
141 IOHANDLE File = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
142 ASSERT_TRUE(File);
143 EXPECT_FALSE(io_close(File));
144 EXPECT_TRUE(fs_removedir(Info.m_aFilename)); // Cannot remove file with directory removal function.
145 EXPECT_FALSE(fs_remove(Info.m_aFilename));
146}
147
148TEST(Filesystem, DeleteNonexistentFile)
149{
150 CTestInfo Info;
151
152 EXPECT_FALSE(fs_is_file(Info.m_aFilename));
153 EXPECT_FALSE(fs_remove(Info.m_aFilename)); // Can delete a file that does not exist.
154}
155
156TEST(Filesystem, DeleteNonexistentDirectory)
157{
158 CTestInfo Info;
159
160 EXPECT_FALSE(fs_is_dir(Info.m_aFilename));
161 EXPECT_FALSE(fs_removedir(Info.m_aFilename)); // Can delete a folder that does not exist.
162}
163
164TEST(Filesystem, DeleteOpenFile)
165{
166 CTestInfo Info;
167
168 IOHANDLE File = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
169 ASSERT_TRUE(File);
170
171 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
172 EXPECT_FALSE(fs_remove(Info.m_aFilename)); // Can delete a file that has open handle.
173 EXPECT_FALSE(fs_is_file(Info.m_aFilename)); // File should be gone immediately even before the last file handle is closed.
174
175 EXPECT_FALSE(io_close(File));
176}
177
178TEST(Filesystem, DeleteOpenFileMultipleHandles)
179{
180 CTestInfo Info;
181
182 IOHANDLE FileWrite1 = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
183 ASSERT_TRUE(FileWrite1);
184
185 IOHANDLE FileRead1 = io_open(filename: Info.m_aFilename, flags: IOFLAG_READ);
186 ASSERT_TRUE(FileRead1);
187
188 IOHANDLE FileWrite2 = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
189 ASSERT_TRUE(FileWrite2);
190
191 IOHANDLE FileRead2 = io_open(filename: Info.m_aFilename, flags: IOFLAG_READ);
192 ASSERT_TRUE(FileRead2);
193
194 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
195 EXPECT_FALSE(fs_remove(Info.m_aFilename)); // Can delete a file that has multiple open handles.
196 EXPECT_FALSE(fs_is_file(Info.m_aFilename)); // File should be gone immediately even before the last file handle is closed.
197
198 EXPECT_FALSE(io_close(FileWrite1));
199 EXPECT_FALSE(io_close(FileRead1));
200 EXPECT_FALSE(io_close(FileWrite2));
201 EXPECT_FALSE(io_close(FileRead2));
202}
203
204TEST(Filesystem, RenameFile)
205{
206 char aNewFilename[IO_MAX_PATH_LENGTH];
207 CTestInfo Info;
208 Info.Filename(pBuffer: aNewFilename, BufferLength: sizeof(aNewFilename), pSuffix: ".renamed");
209
210 IOHANDLE FileWrite = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
211 ASSERT_TRUE(FileWrite);
212 EXPECT_FALSE(io_close(FileWrite));
213
214 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
215 EXPECT_FALSE(fs_rename(Info.m_aFilename, aNewFilename));
216 EXPECT_FALSE(fs_is_file(Info.m_aFilename));
217 EXPECT_TRUE(fs_is_file(aNewFilename));
218
219 EXPECT_FALSE(fs_remove(aNewFilename));
220}
221
222TEST(Filesystem, RenameFolder)
223{
224 char aNewFilename[IO_MAX_PATH_LENGTH];
225 CTestInfo Info;
226 Info.Filename(pBuffer: aNewFilename, BufferLength: sizeof(aNewFilename), pSuffix: ".renamed");
227
228 EXPECT_FALSE(fs_makedir(Info.m_aFilename));
229
230 EXPECT_TRUE(fs_is_dir(Info.m_aFilename));
231 EXPECT_FALSE(fs_rename(Info.m_aFilename, aNewFilename));
232 EXPECT_FALSE(fs_is_dir(Info.m_aFilename));
233 EXPECT_TRUE(fs_is_dir(aNewFilename));
234
235 EXPECT_FALSE(fs_removedir(aNewFilename));
236}
237
238TEST(Filesystem, RenameOpenFileSource)
239{
240 char aNewFilename[IO_MAX_PATH_LENGTH];
241 CTestInfo Info;
242 Info.Filename(pBuffer: aNewFilename, BufferLength: sizeof(aNewFilename), pSuffix: ".renamed");
243
244 IOHANDLE FileWrite = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
245 ASSERT_TRUE(FileWrite);
246
247 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
248 EXPECT_FALSE(fs_rename(Info.m_aFilename, aNewFilename)); // Can rename a file that has open handle.
249 EXPECT_FALSE(fs_is_file(Info.m_aFilename)); // Rename should take effect immediately.
250 EXPECT_TRUE(fs_is_file(aNewFilename));
251
252 EXPECT_FALSE(io_close(FileWrite));
253
254 EXPECT_FALSE(fs_remove(aNewFilename));
255}
256
257TEST(Filesystem, RenameOpenFileSourceMultipleHandles)
258{
259 char aNewFilename[IO_MAX_PATH_LENGTH];
260 CTestInfo Info;
261 Info.Filename(pBuffer: aNewFilename, BufferLength: sizeof(aNewFilename), pSuffix: ".renamed");
262
263 IOHANDLE FileWrite1 = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
264 ASSERT_TRUE(FileWrite1);
265
266 IOHANDLE FileRead1 = io_open(filename: Info.m_aFilename, flags: IOFLAG_READ);
267 ASSERT_TRUE(FileRead1);
268
269 IOHANDLE FileWrite2 = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
270 ASSERT_TRUE(FileWrite2);
271
272 IOHANDLE FileRead2 = io_open(filename: Info.m_aFilename, flags: IOFLAG_READ);
273 ASSERT_TRUE(FileRead2);
274
275 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
276 EXPECT_FALSE(fs_rename(Info.m_aFilename, aNewFilename)); // Can rename a file that has multiple open handles.
277 EXPECT_FALSE(fs_is_file(Info.m_aFilename)); // Rename should take effect immediately.
278 EXPECT_TRUE(fs_is_file(aNewFilename));
279
280 EXPECT_FALSE(io_close(FileWrite1));
281 EXPECT_FALSE(io_close(FileRead1));
282 EXPECT_FALSE(io_close(FileWrite2));
283 EXPECT_FALSE(io_close(FileRead2));
284
285 EXPECT_FALSE(fs_remove(aNewFilename));
286}
287
288TEST(Filesystem, RenameTargetFileExists)
289{
290 char aNewFilename[IO_MAX_PATH_LENGTH];
291 CTestInfo Info;
292 Info.Filename(pBuffer: aNewFilename, BufferLength: sizeof(aNewFilename), pSuffix: ".renamed");
293
294 IOHANDLE FileWrite1 = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
295 ASSERT_TRUE(FileWrite1);
296 EXPECT_FALSE(io_close(FileWrite1));
297
298 IOHANDLE FileWrite2 = io_open(filename: aNewFilename, flags: IOFLAG_WRITE);
299 ASSERT_TRUE(FileWrite2);
300 EXPECT_FALSE(io_close(FileWrite2));
301
302 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
303 EXPECT_TRUE(fs_is_file(aNewFilename));
304 EXPECT_FALSE(fs_rename(Info.m_aFilename, aNewFilename)); // Renaming can overwrite the existing target file if it has no open handles.
305 EXPECT_FALSE(fs_is_file(Info.m_aFilename));
306 EXPECT_TRUE(fs_is_file(aNewFilename));
307
308 EXPECT_FALSE(fs_remove(aNewFilename));
309}
310
311TEST(Filesystem, RenameOpenFileDeleteTarget)
312{
313 char aNewFilename[IO_MAX_PATH_LENGTH];
314 CTestInfo Info;
315 Info.Filename(pBuffer: aNewFilename, BufferLength: sizeof(aNewFilename), pSuffix: ".renamed");
316
317 IOHANDLE FileWrite1 = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
318 ASSERT_TRUE(FileWrite1);
319 EXPECT_FALSE(io_close(FileWrite1));
320
321 IOHANDLE FileWrite2 = io_open(filename: aNewFilename, flags: IOFLAG_WRITE);
322 ASSERT_TRUE(FileWrite2);
323 EXPECT_FALSE(io_close(FileWrite2));
324
325 IOHANDLE FileRead = io_open(filename: aNewFilename, flags: IOFLAG_READ);
326 ASSERT_TRUE(FileRead);
327
328 EXPECT_TRUE(fs_is_file(Info.m_aFilename));
329 EXPECT_TRUE(fs_is_file(aNewFilename));
330 EXPECT_FALSE(fs_remove(aNewFilename)); // Target file must be deleted else rename fails on Windows when target file has open handle.
331 EXPECT_FALSE(fs_rename(Info.m_aFilename, aNewFilename));
332 EXPECT_FALSE(fs_is_file(Info.m_aFilename));
333 EXPECT_TRUE(fs_is_file(aNewFilename));
334
335 EXPECT_FALSE(io_close(FileRead));
336
337 EXPECT_FALSE(fs_remove(aNewFilename));
338}
339