Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/common-tests/path_tests.cpp
4214 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "common/path.h"
5
#include "common/types.h"
6
7
#include <gtest/gtest.h>
8
9
TEST(Path, ToNativePath)
10
{
11
ASSERT_EQ(Path::ToNativePath(""), "");
12
13
#ifdef _WIN32
14
ASSERT_EQ(Path::ToNativePath("foo"), "foo");
15
ASSERT_EQ(Path::ToNativePath("foo\\"), "foo");
16
ASSERT_EQ(Path::ToNativePath("foo\\\\bar"), "foo\\bar");
17
ASSERT_EQ(Path::ToNativePath("foo\\bar"), "foo\\bar");
18
ASSERT_EQ(Path::ToNativePath("foo\\bar\\baz"), "foo\\bar\\baz");
19
ASSERT_EQ(Path::ToNativePath("foo\\bar/baz"), "foo\\bar\\baz");
20
ASSERT_EQ(Path::ToNativePath("foo/bar/baz"), "foo\\bar\\baz");
21
ASSERT_EQ(Path::ToNativePath("foo/🙃bar/b🙃az"), "foo\\🙃bar\\b🙃az");
22
ASSERT_EQ(Path::ToNativePath("\\\\foo\\bar\\baz"), "\\\\foo\\bar\\baz");
23
#else
24
ASSERT_EQ(Path::ToNativePath("foo"), "foo");
25
ASSERT_EQ(Path::ToNativePath("foo/"), "foo");
26
ASSERT_EQ(Path::ToNativePath("foo//bar"), "foo/bar");
27
ASSERT_EQ(Path::ToNativePath("foo/bar"), "foo/bar");
28
ASSERT_EQ(Path::ToNativePath("foo/bar/baz"), "foo/bar/baz");
29
ASSERT_EQ(Path::ToNativePath("/foo/bar/baz"), "/foo/bar/baz");
30
#endif
31
}
32
33
TEST(Path, IsAbsolute)
34
{
35
ASSERT_FALSE(Path::IsAbsolute(""));
36
ASSERT_FALSE(Path::IsAbsolute("foo"));
37
ASSERT_FALSE(Path::IsAbsolute("foo/bar"));
38
ASSERT_FALSE(Path::IsAbsolute("foo/b🙃ar"));
39
#ifdef _WIN32
40
ASSERT_TRUE(Path::IsAbsolute("C:\\foo/bar"));
41
ASSERT_TRUE(Path::IsAbsolute("C://foo\\bar"));
42
ASSERT_FALSE(Path::IsAbsolute("\\foo/bar"));
43
ASSERT_TRUE(Path::IsAbsolute("\\\\foo\\bar\\baz"));
44
ASSERT_TRUE(Path::IsAbsolute("C:\\"));
45
ASSERT_TRUE(Path::IsAbsolute("C:\\Path"));
46
ASSERT_TRUE(Path::IsAbsolute("C:\\Path\\Subdirectory"));
47
ASSERT_TRUE(Path::IsAbsolute("C:/"));
48
ASSERT_TRUE(Path::IsAbsolute("C:/Path"));
49
ASSERT_TRUE(Path::IsAbsolute("C:/Path/Subdirectory"));
50
ASSERT_FALSE(Path::IsAbsolute(""));
51
ASSERT_FALSE(Path::IsAbsolute("C:"));
52
ASSERT_FALSE(Path::IsAbsolute("Path"));
53
ASSERT_FALSE(Path::IsAbsolute("Path/Subdirectory"));
54
#else
55
ASSERT_TRUE(Path::IsAbsolute("/foo/bar"));
56
ASSERT_TRUE(Path::IsAbsolute("/"));
57
ASSERT_TRUE(Path::IsAbsolute("/path"));
58
ASSERT_TRUE(Path::IsAbsolute("/path/subdirectory"));
59
ASSERT_FALSE(Path::IsAbsolute(""));
60
ASSERT_FALSE(Path::IsAbsolute("path"));
61
ASSERT_FALSE(Path::IsAbsolute("path/subdirectory"));
62
#endif
63
}
64
65
TEST(Path, Canonicalize)
66
{
67
ASSERT_EQ(Path::Canonicalize(""), Path::ToNativePath(""));
68
ASSERT_EQ(Path::Canonicalize("foo/bar/../baz"), Path::ToNativePath("foo/baz"));
69
ASSERT_EQ(Path::Canonicalize("foo/bar/./baz"), Path::ToNativePath("foo/bar/baz"));
70
ASSERT_EQ(Path::Canonicalize("foo/./bar/./baz"), Path::ToNativePath("foo/bar/baz"));
71
ASSERT_EQ(Path::Canonicalize("foo/bar/../baz/../foo"), Path::ToNativePath("foo/foo"));
72
ASSERT_EQ(Path::Canonicalize("foo/bar/../baz/./foo"), Path::ToNativePath("foo/baz/foo"));
73
ASSERT_EQ(Path::Canonicalize("./foo"), Path::ToNativePath("foo"));
74
ASSERT_EQ(Path::Canonicalize("../foo"), Path::ToNativePath("../foo"));
75
ASSERT_EQ(Path::Canonicalize("foo/b🙃ar/../b🙃az/./foo"), Path::ToNativePath("foo/b🙃az/foo"));
76
ASSERT_EQ(Path::Canonicalize("ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/b🙃az/../foℹ︎o"),
77
Path::ToNativePath("ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/foℹ︎o"));
78
#ifdef _WIN32
79
ASSERT_EQ(Path::Canonicalize("C:\\foo\\bar\\..\\baz\\.\\foo"), "C:\\foo\\baz\\foo");
80
ASSERT_EQ(Path::Canonicalize("C:/foo\\bar\\..\\baz\\.\\foo"), "C:\\foo\\baz\\foo");
81
ASSERT_EQ(Path::Canonicalize("foo\\bar\\..\\baz\\.\\foo"), "foo\\baz\\foo");
82
ASSERT_EQ(Path::Canonicalize("foo\\bar/..\\baz/.\\foo"), "foo\\baz\\foo");
83
ASSERT_EQ(Path::Canonicalize("\\\\foo\\bar\\baz/..\\foo"), "\\\\foo\\bar\\foo");
84
#else
85
ASSERT_EQ(Path::Canonicalize("/foo/bar/../baz/./foo"), "/foo/baz/foo");
86
#endif
87
}
88
89
TEST(Path, Combine)
90
{
91
ASSERT_EQ(Path::Combine("", ""), Path::ToNativePath(""));
92
ASSERT_EQ(Path::Combine("foo", "bar"), Path::ToNativePath("foo/bar"));
93
ASSERT_EQ(Path::Combine("foo/bar", "baz"), Path::ToNativePath("foo/bar/baz"));
94
ASSERT_EQ(Path::Combine("foo/bar", "../baz"), Path::ToNativePath("foo/bar/../baz"));
95
ASSERT_EQ(Path::Combine("foo/bar/", "/baz/"), Path::ToNativePath("foo/bar/baz"));
96
ASSERT_EQ(Path::Combine("foo//bar", "baz/"), Path::ToNativePath("foo/bar/baz"));
97
ASSERT_EQ(Path::Combine("foo//ba🙃r", "b🙃az/"), Path::ToNativePath("foo/ba🙃r/b🙃az"));
98
#ifdef _WIN32
99
ASSERT_EQ(Path::Combine("C:\\foo\\bar", "baz"), "C:\\foo\\bar\\baz");
100
ASSERT_EQ(Path::Combine("\\\\server\\foo\\bar", "baz"), "\\\\server\\foo\\bar\\baz");
101
ASSERT_EQ(Path::Combine("foo\\bar", "baz"), "foo\\bar\\baz");
102
ASSERT_EQ(Path::Combine("foo\\bar\\", "baz"), "foo\\bar\\baz");
103
ASSERT_EQ(Path::Combine("foo/bar\\", "\\baz"), "foo\\bar\\baz");
104
ASSERT_EQ(Path::Combine("\\\\foo\\bar", "baz"), "\\\\foo\\bar\\baz");
105
#else
106
ASSERT_EQ(Path::Combine("/foo/bar", "baz"), "/foo/bar/baz");
107
#endif
108
}
109
110
TEST(Path, AppendDirectory)
111
{
112
ASSERT_EQ(Path::AppendDirectory("foo/bar", "baz"), Path::ToNativePath("foo/baz/bar"));
113
ASSERT_EQ(Path::AppendDirectory("", "baz"), Path::ToNativePath("baz"));
114
ASSERT_EQ(Path::AppendDirectory("", ""), Path::ToNativePath(""));
115
ASSERT_EQ(Path::AppendDirectory("foo/bar", "🙃"), Path::ToNativePath("foo/🙃/bar"));
116
#ifdef _WIN32
117
ASSERT_EQ(Path::AppendDirectory("foo\\bar", "baz"), "foo\\baz\\bar");
118
ASSERT_EQ(Path::AppendDirectory("\\\\foo\\bar", "baz"), "\\\\foo\\baz\\bar");
119
#else
120
ASSERT_EQ(Path::AppendDirectory("/foo/bar", "baz"), "/foo/baz/bar");
121
#endif
122
}
123
124
TEST(Path, MakeRelative)
125
{
126
ASSERT_EQ(Path::MakeRelative("", ""), Path::ToNativePath(""));
127
ASSERT_EQ(Path::MakeRelative("foo", ""), Path::ToNativePath("foo"));
128
ASSERT_EQ(Path::MakeRelative("", "foo"), Path::ToNativePath(""));
129
ASSERT_EQ(Path::MakeRelative("foo", "bar"), Path::ToNativePath("foo"));
130
131
#ifdef _WIN32
132
#define A "C:\\"
133
#else
134
#define A "/"
135
#endif
136
137
ASSERT_EQ(Path::MakeRelative(A "foo", A "bar"), Path::ToNativePath("../foo"));
138
ASSERT_EQ(Path::MakeRelative(A "foo/bar", A "foo"), Path::ToNativePath("bar"));
139
ASSERT_EQ(Path::MakeRelative(A "foo/bar", A "foo/baz"), Path::ToNativePath("../bar"));
140
ASSERT_EQ(Path::MakeRelative(A "foo/b🙃ar", A "foo/b🙃az"), Path::ToNativePath("../b🙃ar"));
141
ASSERT_EQ(Path::MakeRelative(A "f🙃oo/b🙃ar", A "f🙃oo/b🙃az"), Path::ToNativePath("../b🙃ar"));
142
ASSERT_EQ(
143
Path::MakeRelative(A "ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/b🙃ar", A "ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱/b🙃az"),
144
Path::ToNativePath("../b🙃ar"));
145
146
#undef A
147
148
#ifdef _WIN32
149
ASSERT_EQ(Path::MakeRelative("\\\\foo\\bar\\baz\\foo", "\\\\foo\\bar\\baz"), "foo");
150
ASSERT_EQ(Path::MakeRelative("\\\\foo\\bar\\foo", "\\\\foo\\bar\\baz"), "..\\foo");
151
ASSERT_EQ(Path::MakeRelative("\\\\foo\\bar\\foo", "\\\\other\\bar\\foo"), "\\\\foo\\bar\\foo");
152
#endif
153
}
154
155
TEST(Path, GetExtension)
156
{
157
ASSERT_EQ(Path::GetExtension("foo"), "");
158
ASSERT_EQ(Path::GetExtension("foo.txt"), "txt");
159
ASSERT_EQ(Path::GetExtension("foo.t🙃t"), "t🙃t");
160
ASSERT_EQ(Path::GetExtension("foo."), "");
161
ASSERT_EQ(Path::GetExtension("a/b/foo.txt"), "txt");
162
ASSERT_EQ(Path::GetExtension("a/b/foo"), "");
163
}
164
165
TEST(Path, GetFileName)
166
{
167
ASSERT_EQ(Path::GetFileName(""), "");
168
ASSERT_EQ(Path::GetFileName("foo"), "foo");
169
ASSERT_EQ(Path::GetFileName("foo.txt"), "foo.txt");
170
ASSERT_EQ(Path::GetFileName("foo"), "foo");
171
ASSERT_EQ(Path::GetFileName("foo/bar/."), ".");
172
ASSERT_EQ(Path::GetFileName("foo/bar/baz"), "baz");
173
ASSERT_EQ(Path::GetFileName("foo/bar/baz.txt"), "baz.txt");
174
#ifdef _WIN32
175
ASSERT_EQ(Path::GetFileName("foo/bar\\baz"), "baz");
176
ASSERT_EQ(Path::GetFileName("foo\\bar\\baz.txt"), "baz.txt");
177
#endif
178
}
179
180
TEST(Path, GetFileTitle)
181
{
182
ASSERT_EQ(Path::GetFileTitle(""), "");
183
ASSERT_EQ(Path::GetFileTitle("foo"), "foo");
184
ASSERT_EQ(Path::GetFileTitle("foo.txt"), "foo");
185
ASSERT_EQ(Path::GetFileTitle("foo/bar/."), "");
186
ASSERT_EQ(Path::GetFileTitle("foo/bar/baz"), "baz");
187
ASSERT_EQ(Path::GetFileTitle("foo/bar/baz.txt"), "baz");
188
#ifdef _WIN32
189
ASSERT_EQ(Path::GetFileTitle("foo/bar\\baz"), "baz");
190
ASSERT_EQ(Path::GetFileTitle("foo\\bar\\baz.txt"), "baz");
191
#endif
192
}
193
194
TEST(Path, GetDirectory)
195
{
196
ASSERT_EQ(Path::GetDirectory(""), "");
197
ASSERT_EQ(Path::GetDirectory("foo"), "");
198
ASSERT_EQ(Path::GetDirectory("foo.txt"), "");
199
ASSERT_EQ(Path::GetDirectory("foo/bar/."), "foo/bar");
200
ASSERT_EQ(Path::GetDirectory("foo/bar/baz"), "foo/bar");
201
ASSERT_EQ(Path::GetDirectory("foo/bar/baz.txt"), "foo/bar");
202
#ifdef _WIN32
203
ASSERT_EQ(Path::GetDirectory("foo\\bar\\baz"), "foo\\bar");
204
ASSERT_EQ(Path::GetDirectory("foo\\bar/baz.txt"), "foo\\bar");
205
#endif
206
}
207
208
TEST(Path, ChangeFileName)
209
{
210
ASSERT_EQ(Path::ChangeFileName("", ""), Path::ToNativePath(""));
211
ASSERT_EQ(Path::ChangeFileName("", "bar"), Path::ToNativePath("bar"));
212
ASSERT_EQ(Path::ChangeFileName("bar", ""), Path::ToNativePath(""));
213
ASSERT_EQ(Path::ChangeFileName("foo/bar", ""), Path::ToNativePath("foo"));
214
ASSERT_EQ(Path::ChangeFileName("foo/", "bar"), Path::ToNativePath("foo/bar"));
215
ASSERT_EQ(Path::ChangeFileName("foo/bar", "baz"), Path::ToNativePath("foo/baz"));
216
ASSERT_EQ(Path::ChangeFileName("foo//bar", "baz"), Path::ToNativePath("foo/baz"));
217
ASSERT_EQ(Path::ChangeFileName("foo//bar.txt", "baz.txt"), Path::ToNativePath("foo/baz.txt"));
218
ASSERT_EQ(Path::ChangeFileName("foo//ba🙃r.txt", "ba🙃z.txt"), Path::ToNativePath("foo/ba🙃z.txt"));
219
#ifdef _WIN32
220
ASSERT_EQ(Path::ChangeFileName("foo/bar", "baz"), "foo\\baz");
221
ASSERT_EQ(Path::ChangeFileName("foo//bar\\foo", "baz"), "foo\\bar\\baz");
222
ASSERT_EQ(Path::ChangeFileName("\\\\foo\\bar\\foo", "baz"), "\\\\foo\\bar\\baz");
223
#else
224
ASSERT_EQ(Path::ChangeFileName("/foo/bar", "baz"), "/foo/baz");
225
#endif
226
}
227
228
TEST(Path, SanitizeFileName)
229
{
230
ASSERT_EQ(Path::SanitizeFileName("foo"), "foo");
231
ASSERT_EQ(Path::SanitizeFileName("foo/bar"), "foo_bar");
232
ASSERT_EQ(Path::SanitizeFileName("f🙃o"), "f🙃o");
233
ASSERT_EQ(Path::SanitizeFileName("ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱"), "ŻąłóРстуぬねのはen🍪⟑η∏☉ⴤℹ︎∩₲ ₱⟑♰⫳🐱");
234
ASSERT_EQ(Path::SanitizeFileName("abcdefghijlkmnopqrstuvwxyz-0123456789+&=_[]{}"),
235
"abcdefghijlkmnopqrstuvwxyz-0123456789+&=_[]{}");
236
ASSERT_EQ(Path::SanitizeFileName("some*path**with*asterisks"), "some_path__with_asterisks");
237
#ifdef _WIN32
238
ASSERT_EQ(Path::SanitizeFileName("foo:"), "foo_");
239
ASSERT_EQ(Path::SanitizeFileName("foo:bar."), "foo_bar_");
240
ASSERT_EQ(Path::SanitizeFileName("foo\\bar"), "foo_bar");
241
ASSERT_EQ(Path::SanitizeFileName("foo>bar"), "foo_bar");
242
ASSERT_EQ(Path::SanitizeFileName("foo\\bar", false), "foo\\bar");
243
#endif
244
ASSERT_EQ(Path::SanitizeFileName("foo/bar", false), "foo/bar");
245
}
246
247
TEST(Path, RemoveLengthLimits)
248
{
249
#ifdef _WIN32
250
ASSERT_EQ(Path::RemoveLengthLimits("C:\\foo"), "\\\\?\\C:\\foo");
251
ASSERT_EQ(Path::RemoveLengthLimits("\\\\foo\\bar\\baz"), "\\\\?\\UNC\\foo\\bar\\baz");
252
#else
253
ASSERT_EQ(Path::RemoveLengthLimits("/foo/bar/baz"), "/foo/bar/baz");
254
#endif
255
}
256
257
#if 0
258
259
// Relies on presence of files.
260
TEST(Path, RealPath)
261
{
262
#ifdef _WIN32
263
ASSERT_EQ(Path::RealPath("C:\\Users\\Me\\Desktop\\foo\\baz"), "C:\\Users\\Me\\Desktop\\foo\\bar\\baz");
264
#else
265
ASSERT_EQ(Path::RealPath("/lib/foo/bar"), "/usr/lib/foo/bar");
266
#endif
267
}
268
269
#endif
270
271
TEST(Path, CreateFileURL)
272
{
273
#ifdef _WIN32
274
ASSERT_EQ(Path::CreateFileURL("C:\\foo\\bar"), "file:///C:/foo/bar");
275
ASSERT_EQ(Path::CreateFileURL("\\\\server\\share\\file.txt"), "file://server/share/file.txt");
276
#else
277
ASSERT_EQ(Path::CreateFileURL("/foo/bar"), "file:///foo/bar");
278
#endif
279
}
280
281
TEST(Path, URLEncode)
282
{
283
// Basic cases
284
ASSERT_EQ(Path::URLEncode("hello world"), "hello%20world");
285
ASSERT_EQ(Path::URLEncode(""), "");
286
ASSERT_EQ(Path::URLEncode("abcABC123"), "abcABC123");
287
288
// Special characters
289
ASSERT_EQ(Path::URLEncode("!@#$%^&*()"), "%21%40%23%24%25%5E%26%2A%28%29");
290
ASSERT_EQ(Path::URLEncode("[]{}<>"), "%5B%5D%7B%7D%3C%3E");
291
ASSERT_EQ(Path::URLEncode(",./?;:'\""), "%2C.%2F%3F%3B%3A%27%22");
292
293
// Unicode characters
294
ASSERT_EQ(Path::URLEncode("こんにちは"), "%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF");
295
ASSERT_EQ(Path::URLEncode("über"), "%C3%BCber");
296
297
// Additional special characters
298
ASSERT_EQ(Path::URLEncode("=&?"), "%3D%26%3F");
299
ASSERT_EQ(Path::URLEncode("\\|`"), "%5C%7C%60");
300
ASSERT_EQ(Path::URLEncode("§±€"), "%C2%A7%C2%B1%E2%82%AC");
301
ASSERT_EQ(Path::URLEncode("%20%2F%3F"), "%2520%252F%253F");
302
ASSERT_EQ(Path::URLEncode("tab\tline\nreturn\r"), "tab%09line%0Areturn%0D");
303
304
// Mixed content
305
ASSERT_EQ(Path::URLEncode("path/to/my file.txt"), "path%2Fto%2Fmy%20file.txt");
306
ASSERT_EQ(Path::URLEncode("[email protected]"), "user%2Bname%40example.com");
307
}
308
309
TEST(Path, URLDecode)
310
{
311
// Basic cases
312
ASSERT_EQ(Path::URLDecode("hello%20world"), "hello world");
313
ASSERT_EQ(Path::URLDecode(""), "");
314
ASSERT_EQ(Path::URLDecode("abcABC123"), "abcABC123");
315
316
// Special characters
317
ASSERT_EQ(Path::URLDecode("%21%40%23%24%25%5E%26%2A%28%29"), "!@#$%^&*()");
318
ASSERT_EQ(Path::URLDecode("%5B%5D%7B%7D%3C%3E"), "[]{}<>");
319
ASSERT_EQ(Path::URLDecode("%2C%2F%3F%3B%3A%27%22"), ",/?;:'\"");
320
321
// Additional special characters
322
ASSERT_EQ(Path::URLDecode("%3D%26%3F"), "=&?");
323
ASSERT_EQ(Path::URLDecode("%5C%7C%60"), "\\|`");
324
ASSERT_EQ(Path::URLDecode("%C2%A7%C2%B1%E2%82%AC"), "§±€");
325
ASSERT_EQ(Path::URLDecode("%2520%252F%253F"), "%20%2F%3F");
326
ASSERT_EQ(Path::URLDecode("tab%09line%0Areturn%0D"), "tab\tline\nreturn\r");
327
328
// Unicode characters
329
ASSERT_EQ(Path::URLDecode("%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF"), "こんにちは");
330
ASSERT_EQ(Path::URLDecode("%C3%BCber"), "über");
331
332
// Mixed content
333
ASSERT_EQ(Path::URLDecode("path%2Fto%2Fmy%20file.txt"), "path/to/my file.txt");
334
ASSERT_EQ(Path::URLDecode("user%2Bname%40example.com"), "[email protected]");
335
336
// Invalid decode cases - decoder should stop at first error
337
ASSERT_EQ(Path::URLDecode("hello%2G"), "hello"); // Invalid hex char 'G'
338
ASSERT_EQ(Path::URLDecode("test%"), "test"); // Incomplete escape sequence
339
ASSERT_EQ(Path::URLDecode("path%%20name"), "path"); // Invalid % followed by valid sequence
340
ASSERT_EQ(Path::URLDecode("abc%2"), "abc"); // Truncated escape sequence
341
}
342
343