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