1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/files/file_path.h"
6
7 #include <stddef.h>
8
9 #include <sstream>
10
11 #include "base/files/safe_base_name.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "build/build_config.h"
14 #include "build/buildflag.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "testing/platform_test.h"
17
18 #if BUILDFLAG(ENABLE_BASE_TRACING)
19 #include "third_party/perfetto/include/perfetto/test/traced_value_test_support.h" // no-presubmit-check nogncheck
20 #endif // BUILDFLAG(ENABLE_BASE_TRACING)
21
22 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
23 #include "base/test/scoped_locale.h"
24 #endif
25
26 // This macro helps test `FILE_PATH_LITERAL` macro expansion.
27 #define TEST_FILE "TestFile"
28
29 // This macro helps avoid wrapped lines in the test structs.
30 #define FPL(x) FILE_PATH_LITERAL(x)
31
32 // This macro constructs strings which can contain NULs.
33 #define FPS(x) FilePath::StringType(FPL(x), std::size(FPL(x)) - 1)
34
35 namespace base {
36
37 struct UnaryTestData {
38 FilePath::StringPieceType input;
39 FilePath::StringPieceType expected;
40 };
41
42 struct UnaryBooleanTestData {
43 FilePath::StringPieceType input;
44 bool expected;
45 };
46
47 struct BinaryTestData {
48 FilePath::StringPieceType inputs[2];
49 FilePath::StringPieceType expected;
50 };
51
52 struct BinaryBooleanTestData {
53 FilePath::StringPieceType inputs[2];
54 bool expected;
55 };
56
57 struct BinaryIntTestData {
58 FilePath::StringPieceType inputs[2];
59 int expected;
60 };
61
62 struct UTF8TestData {
63 FilePath::StringPieceType native;
64 StringPiece utf8;
65 };
66
67 // file_util winds up using autoreleased objects on the Mac, so this needs
68 // to be a PlatformTest
69 typedef PlatformTest FilePathTest;
70
TEST_F(FilePathTest,DirName)71 TEST_F(FilePathTest, DirName) {
72 const struct UnaryTestData cases[] = {
73 {FPL(""), FPL(".")},
74 {FPL("aa"), FPL(".")},
75 {FPL("/aa/bb"), FPL("/aa")},
76 {FPL("/aa/bb/"), FPL("/aa")},
77 {FPL("/aa/bb//"), FPL("/aa")},
78 {FPL("/aa/bb/ccc"), FPL("/aa/bb")},
79 {FPL("/aa"), FPL("/")},
80 {FPL("/aa/"), FPL("/")},
81 {FPL("/"), FPL("/")},
82 {FPL("//"), FPL("//")},
83 {FPL("///"), FPL("/")},
84 {FPL("aa/"), FPL(".")},
85 {FPL("aa/bb"), FPL("aa")},
86 {FPL("aa/bb/"), FPL("aa")},
87 {FPL("aa/bb//"), FPL("aa")},
88 {FPL("aa//bb//"), FPL("aa")},
89 {FPL("aa//bb/"), FPL("aa")},
90 {FPL("aa//bb"), FPL("aa")},
91 {FPL("//aa/bb"), FPL("//aa")},
92 {FPL("//aa/"), FPL("//")},
93 {FPL("//aa"), FPL("//")},
94 #if BUILDFLAG(IS_POSIX)
95 {FPL("///aa/"), FPL("/")},
96 {FPL("///aa"), FPL("/")},
97 {FPL("///"), FPL("/")},
98 #endif // BUILDFLAG(IS_POSIX)
99 {FPL("0:"), FPL(".")},
100 {FPL("@:"), FPL(".")},
101 {FPL("[:"), FPL(".")},
102 {FPL("`:"), FPL(".")},
103 {FPL("{:"), FPL(".")},
104 {FPL("\xB3:"), FPL(".")},
105 {FPL("\xC5:"), FPL(".")},
106 {FPL("/aa/../bb/cc"), FPL("/aa/../bb")},
107 #if BUILDFLAG(IS_WIN)
108 {FPL("\x0143:"), FPL(".")},
109 #endif // BUILDFLAG(IS_WIN)
110 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
111 {FPL("c:"), FPL("c:")},
112 {FPL("C:"), FPL("C:")},
113 {FPL("A:"), FPL("A:")},
114 {FPL("Z:"), FPL("Z:")},
115 {FPL("a:"), FPL("a:")},
116 {FPL("z:"), FPL("z:")},
117 {FPL("c:aa"), FPL("c:")},
118 {FPL("c:/"), FPL("c:/")},
119 {FPL("c://"), FPL("c://")},
120 {FPL("c:///"), FPL("c:/")},
121 {FPL("c:/aa"), FPL("c:/")},
122 {FPL("c:/aa/"), FPL("c:/")},
123 {FPL("c:/aa/bb"), FPL("c:/aa")},
124 {FPL("c:aa/bb"), FPL("c:aa")},
125 #endif // FILE_PATH_USES_DRIVE_LETTERS
126 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
127 {FPL("\\aa\\bb"), FPL("\\aa")},
128 {FPL("\\aa\\bb\\"), FPL("\\aa")},
129 {FPL("\\aa\\bb\\\\"), FPL("\\aa")},
130 {FPL("\\aa\\bb\\ccc"), FPL("\\aa\\bb")},
131 {FPL("\\aa"), FPL("\\")},
132 {FPL("\\aa\\"), FPL("\\")},
133 {FPL("\\"), FPL("\\")},
134 {FPL("\\\\"), FPL("\\\\")},
135 {FPL("\\\\\\"), FPL("\\")},
136 {FPL("aa\\"), FPL(".")},
137 {FPL("aa\\bb"), FPL("aa")},
138 {FPL("aa\\bb\\"), FPL("aa")},
139 {FPL("aa\\bb\\\\"), FPL("aa")},
140 {FPL("aa\\\\bb\\\\"), FPL("aa")},
141 {FPL("aa\\\\bb\\"), FPL("aa")},
142 {FPL("aa\\\\bb"), FPL("aa")},
143 {FPL("\\\\aa\\bb"), FPL("\\\\aa")},
144 {FPL("\\\\aa\\"), FPL("\\\\")},
145 {FPL("\\\\aa"), FPL("\\\\")},
146 {FPL("aa\\..\\bb\\c"), FPL("aa\\..\\bb")},
147 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
148 {FPL("c:\\"), FPL("c:\\")},
149 {FPL("c:\\\\"), FPL("c:\\\\")},
150 {FPL("c:\\\\\\"), FPL("c:\\")},
151 {FPL("c:\\aa"), FPL("c:\\")},
152 {FPL("c:\\aa\\"), FPL("c:\\")},
153 {FPL("c:\\aa\\bb"), FPL("c:\\aa")},
154 {FPL("c:aa\\bb"), FPL("c:aa")},
155 #endif // FILE_PATH_USES_DRIVE_LETTERS
156 #endif // FILE_PATH_USES_WIN_SEPARATORS
157 };
158
159 for (size_t i = 0; i < std::size(cases); ++i) {
160 FilePath input(cases[i].input);
161 FilePath observed = input.DirName();
162 EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) <<
163 "i: " << i << ", input: " << input.value();
164 }
165 }
166
TEST_F(FilePathTest,BaseName)167 TEST_F(FilePathTest, BaseName) {
168 const struct UnaryTestData cases[] = {
169 {FPL(""), FPL("")},
170 {FPL("aa"), FPL("aa")},
171 {FPL("/aa/bb"), FPL("bb")},
172 {FPL("/aa/bb/"), FPL("bb")},
173 {FPL("/aa/bb//"), FPL("bb")},
174 {FPL("/aa/bb/ccc"), FPL("ccc")},
175 {FPL("/aa"), FPL("aa")},
176 {FPL("/"), FPL("/")},
177 {FPL("//"), FPL("//")},
178 {FPL("///"), FPL("/")},
179 {FPL("aa/"), FPL("aa")},
180 {FPL("aa/bb"), FPL("bb")},
181 {FPL("aa/bb/"), FPL("bb")},
182 {FPL("aa/bb//"), FPL("bb")},
183 {FPL("aa//bb//"), FPL("bb")},
184 {FPL("aa//bb/"), FPL("bb")},
185 {FPL("aa//bb"), FPL("bb")},
186 {FPL("//aa/bb"), FPL("bb")},
187 {FPL("//aa/"), FPL("aa")},
188 {FPL("//aa"), FPL("aa")},
189 {FPL("0:"), FPL("0:")},
190 {FPL("@:"), FPL("@:")},
191 {FPL("[:"), FPL("[:")},
192 {FPL("`:"), FPL("`:")},
193 {FPL("{:"), FPL("{:")},
194 {FPL("\xB3:"), FPL("\xB3:")},
195 {FPL("\xC5:"), FPL("\xC5:")},
196 #if BUILDFLAG(IS_WIN)
197 {FPL("\x0143:"), FPL("\x0143:")},
198 #endif // BUILDFLAG(IS_WIN)
199 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
200 {FPL("c:"), FPL("")},
201 {FPL("C:"), FPL("")},
202 {FPL("A:"), FPL("")},
203 {FPL("Z:"), FPL("")},
204 {FPL("a:"), FPL("")},
205 {FPL("z:"), FPL("")},
206 {FPL("c:aa"), FPL("aa")},
207 {FPL("c:/"), FPL("/")},
208 {FPL("c://"), FPL("//")},
209 {FPL("c:///"), FPL("/")},
210 {FPL("c:/aa"), FPL("aa")},
211 {FPL("c:/aa/"), FPL("aa")},
212 {FPL("c:/aa/bb"), FPL("bb")},
213 {FPL("c:aa/bb"), FPL("bb")},
214 #endif // FILE_PATH_USES_DRIVE_LETTERS
215 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
216 {FPL("\\aa\\bb"), FPL("bb")},
217 {FPL("\\aa\\bb\\"), FPL("bb")},
218 {FPL("\\aa\\bb\\\\"), FPL("bb")},
219 {FPL("\\aa\\bb\\ccc"), FPL("ccc")},
220 {FPL("\\aa"), FPL("aa")},
221 {FPL("\\"), FPL("\\")},
222 {FPL("\\\\"), FPL("\\\\")},
223 {FPL("\\\\\\"), FPL("\\")},
224 {FPL("aa\\"), FPL("aa")},
225 {FPL("aa\\bb"), FPL("bb")},
226 {FPL("aa\\bb\\"), FPL("bb")},
227 {FPL("aa\\bb\\\\"), FPL("bb")},
228 {FPL("aa\\\\bb\\\\"), FPL("bb")},
229 {FPL("aa\\\\bb\\"), FPL("bb")},
230 {FPL("aa\\\\bb"), FPL("bb")},
231 {FPL("\\\\aa\\bb"), FPL("bb")},
232 {FPL("\\\\aa\\"), FPL("aa")},
233 {FPL("\\\\aa"), FPL("aa")},
234 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
235 {FPL("c:\\"), FPL("\\")},
236 {FPL("c:\\\\"), FPL("\\\\")},
237 {FPL("c:\\\\\\"), FPL("\\")},
238 {FPL("c:\\aa"), FPL("aa")},
239 {FPL("c:\\aa\\"), FPL("aa")},
240 {FPL("c:\\aa\\bb"), FPL("bb")},
241 {FPL("c:aa\\bb"), FPL("bb")},
242 #endif // FILE_PATH_USES_DRIVE_LETTERS
243 #endif // FILE_PATH_USES_WIN_SEPARATORS
244 };
245
246 for (size_t i = 0; i < std::size(cases); ++i) {
247 FilePath input(cases[i].input);
248 FilePath observed = input.BaseName();
249 EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) <<
250 "i: " << i << ", input: " << input.value();
251 }
252 }
253
TEST_F(FilePathTest,Append)254 TEST_F(FilePathTest, Append) {
255 const struct BinaryTestData cases[] = {
256 { { FPL(""), FPL("cc") }, FPL("cc") },
257 { { FPL("."), FPL("ff") }, FPL("ff") },
258 { { FPL("."), FPL("") }, FPL(".") },
259 { { FPL("/"), FPL("cc") }, FPL("/cc") },
260 { { FPL("/aa"), FPL("") }, FPL("/aa") },
261 { { FPL("/aa/"), FPL("") }, FPL("/aa") },
262 { { FPL("//aa"), FPL("") }, FPL("//aa") },
263 { { FPL("//aa/"), FPL("") }, FPL("//aa") },
264 { { FPL("//"), FPL("aa") }, FPL("//aa") },
265 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
266 { { FPL("c:"), FPL("a") }, FPL("c:a") },
267 { { FPL("c:"), FPL("") }, FPL("c:") },
268 { { FPL("c:/"), FPL("a") }, FPL("c:/a") },
269 { { FPL("c://"), FPL("a") }, FPL("c://a") },
270 { { FPL("c:///"), FPL("a") }, FPL("c:/a") },
271 #endif // FILE_PATH_USES_DRIVE_LETTERS
272 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
273 // Append introduces the default separator character, so these test cases
274 // need to be defined with different expected results on platforms that use
275 // different default separator characters.
276 { { FPL("\\"), FPL("cc") }, FPL("\\cc") },
277 { { FPL("\\aa"), FPL("") }, FPL("\\aa") },
278 { { FPL("\\aa\\"), FPL("") }, FPL("\\aa") },
279 { { FPL("\\\\aa"), FPL("") }, FPL("\\\\aa") },
280 { { FPL("\\\\aa\\"), FPL("") }, FPL("\\\\aa") },
281 { { FPL("\\\\"), FPL("aa") }, FPL("\\\\aa") },
282 { { FPL("/aa/bb"), FPL("cc") }, FPL("/aa/bb\\cc") },
283 { { FPL("/aa/bb/"), FPL("cc") }, FPL("/aa/bb\\cc") },
284 { { FPL("aa/bb/"), FPL("cc") }, FPL("aa/bb\\cc") },
285 { { FPL("aa/bb"), FPL("cc") }, FPL("aa/bb\\cc") },
286 { { FPL("a/b"), FPL("c") }, FPL("a/b\\c") },
287 { { FPL("a/b/"), FPL("c") }, FPL("a/b\\c") },
288 { { FPL("//aa"), FPL("bb") }, FPL("//aa\\bb") },
289 { { FPL("//aa/"), FPL("bb") }, FPL("//aa\\bb") },
290 { { FPL("\\aa\\bb"), FPL("cc") }, FPL("\\aa\\bb\\cc") },
291 { { FPL("\\aa\\bb\\"), FPL("cc") }, FPL("\\aa\\bb\\cc") },
292 { { FPL("aa\\bb\\"), FPL("cc") }, FPL("aa\\bb\\cc") },
293 { { FPL("aa\\bb"), FPL("cc") }, FPL("aa\\bb\\cc") },
294 { { FPL("a\\b"), FPL("c") }, FPL("a\\b\\c") },
295 { { FPL("a\\b\\"), FPL("c") }, FPL("a\\b\\c") },
296 { { FPL("\\\\aa"), FPL("bb") }, FPL("\\\\aa\\bb") },
297 { { FPL("\\\\aa\\"), FPL("bb") }, FPL("\\\\aa\\bb") },
298 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
299 { { FPL("c:\\"), FPL("a") }, FPL("c:\\a") },
300 { { FPL("c:\\\\"), FPL("a") }, FPL("c:\\\\a") },
301 { { FPL("c:\\\\\\"), FPL("a") }, FPL("c:\\a") },
302 { { FPL("c:\\"), FPL("") }, FPL("c:\\") },
303 { { FPL("c:\\a"), FPL("b") }, FPL("c:\\a\\b") },
304 { { FPL("c:\\a\\"), FPL("b") }, FPL("c:\\a\\b") },
305 #endif // FILE_PATH_USES_DRIVE_LETTERS
306 #else // FILE_PATH_USES_WIN_SEPARATORS
307 { { FPL("/aa/bb"), FPL("cc") }, FPL("/aa/bb/cc") },
308 { { FPL("/aa/bb/"), FPL("cc") }, FPL("/aa/bb/cc") },
309 { { FPL("aa/bb/"), FPL("cc") }, FPL("aa/bb/cc") },
310 { { FPL("aa/bb"), FPL("cc") }, FPL("aa/bb/cc") },
311 { { FPL("a/b"), FPL("c") }, FPL("a/b/c") },
312 { { FPL("a/b/"), FPL("c") }, FPL("a/b/c") },
313 { { FPL("//aa"), FPL("bb") }, FPL("//aa/bb") },
314 { { FPL("//aa/"), FPL("bb") }, FPL("//aa/bb") },
315 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
316 { { FPL("c:/"), FPL("a") }, FPL("c:/a") },
317 { { FPL("c:/"), FPL("") }, FPL("c:/") },
318 { { FPL("c:/a"), FPL("b") }, FPL("c:/a/b") },
319 { { FPL("c:/a/"), FPL("b") }, FPL("c:/a/b") },
320 #endif // FILE_PATH_USES_DRIVE_LETTERS
321 #endif // FILE_PATH_USES_WIN_SEPARATORS
322 };
323
324 for (size_t i = 0; i < std::size(cases); ++i) {
325 FilePath root(cases[i].inputs[0]);
326 FilePath::StringType leaf(cases[i].inputs[1]);
327 FilePath observed_str = root.Append(leaf);
328 EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_str.value()) <<
329 "i: " << i << ", root: " << root.value() << ", leaf: " << leaf;
330 FilePath observed_path = root.Append(FilePath(leaf));
331 EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_path.value()) <<
332 "i: " << i << ", root: " << root.value() << ", leaf: " << leaf;
333
334 // TODO(erikkay): It would be nice to have a unicode test append value to
335 // handle the case when AppendASCII is passed UTF8
336 #if BUILDFLAG(IS_WIN)
337 std::string ascii = WideToUTF8(leaf);
338 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
339 std::string ascii = leaf;
340 #endif
341 observed_str = root.AppendASCII(ascii);
342 EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_str.value()) <<
343 "i: " << i << ", root: " << root.value() << ", leaf: " << leaf;
344 }
345 }
346
TEST_F(FilePathTest,StripTrailingSeparators)347 TEST_F(FilePathTest, StripTrailingSeparators) {
348 const struct UnaryTestData cases[] = {
349 { FPL(""), FPL("") },
350 { FPL("/"), FPL("/") },
351 { FPL("//"), FPL("//") },
352 { FPL("///"), FPL("/") },
353 { FPL("////"), FPL("/") },
354 { FPL("a/"), FPL("a") },
355 { FPL("a//"), FPL("a") },
356 { FPL("a///"), FPL("a") },
357 { FPL("a////"), FPL("a") },
358 { FPL("/a"), FPL("/a") },
359 { FPL("/a/"), FPL("/a") },
360 { FPL("/a//"), FPL("/a") },
361 { FPL("/a///"), FPL("/a") },
362 { FPL("/a////"), FPL("/a") },
363 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
364 { FPL("c:"), FPL("c:") },
365 { FPL("c:/"), FPL("c:/") },
366 { FPL("c://"), FPL("c://") },
367 { FPL("c:///"), FPL("c:/") },
368 { FPL("c:////"), FPL("c:/") },
369 { FPL("c:/a"), FPL("c:/a") },
370 { FPL("c:/a/"), FPL("c:/a") },
371 { FPL("c:/a//"), FPL("c:/a") },
372 { FPL("c:/a///"), FPL("c:/a") },
373 { FPL("c:/a////"), FPL("c:/a") },
374 #endif // FILE_PATH_USES_DRIVE_LETTERS
375 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
376 { FPL("\\"), FPL("\\") },
377 { FPL("\\\\"), FPL("\\\\") },
378 { FPL("\\\\\\"), FPL("\\") },
379 { FPL("\\\\\\\\"), FPL("\\") },
380 { FPL("a\\"), FPL("a") },
381 { FPL("a\\\\"), FPL("a") },
382 { FPL("a\\\\\\"), FPL("a") },
383 { FPL("a\\\\\\\\"), FPL("a") },
384 { FPL("\\a"), FPL("\\a") },
385 { FPL("\\a\\"), FPL("\\a") },
386 { FPL("\\a\\\\"), FPL("\\a") },
387 { FPL("\\a\\\\\\"), FPL("\\a") },
388 { FPL("\\a\\\\\\\\"), FPL("\\a") },
389 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
390 { FPL("c:\\"), FPL("c:\\") },
391 { FPL("c:\\\\"), FPL("c:\\\\") },
392 { FPL("c:\\\\\\"), FPL("c:\\") },
393 { FPL("c:\\\\\\\\"), FPL("c:\\") },
394 { FPL("c:\\a"), FPL("c:\\a") },
395 { FPL("c:\\a\\"), FPL("c:\\a") },
396 { FPL("c:\\a\\\\"), FPL("c:\\a") },
397 { FPL("c:\\a\\\\\\"), FPL("c:\\a") },
398 { FPL("c:\\a\\\\\\\\"), FPL("c:\\a") },
399 #endif // FILE_PATH_USES_DRIVE_LETTERS
400 #endif // FILE_PATH_USES_WIN_SEPARATORS
401 };
402
403 for (size_t i = 0; i < std::size(cases); ++i) {
404 FilePath input(cases[i].input);
405 FilePath observed = input.StripTrailingSeparators();
406 EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) <<
407 "i: " << i << ", input: " << input.value();
408 }
409 }
410
TEST_F(FilePathTest,IsAbsoluteOrNetwork)411 TEST_F(FilePathTest, IsAbsoluteOrNetwork) {
412 const struct {
413 FilePath::StringPieceType input;
414 bool expected_is_absolute;
415 bool expected_is_network;
416 } cases[] = {
417 { FPL(""), false, false },
418 { FPL("a"), false, false },
419 { FPL("c:"), false, false },
420 { FPL("c:a"), false, false },
421 { FPL("a/b"), false, false },
422 { FPL("//"), true, true },
423 { FPL("//a"), true, true },
424 { FPL("c:a/b"), false, false },
425 { FPL("?:/a"), false, false },
426 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
427 { FPL("/"), false, false },
428 { FPL("/a"), false, false },
429 { FPL("/."), false, false },
430 { FPL("/.."), false, false },
431 { FPL("c:/"), true, false },
432 { FPL("c:/a"), true, false },
433 { FPL("c:/."), true, false },
434 { FPL("c:/.."), true, false },
435 { FPL("C:/a"), true, false },
436 { FPL("d:/a"), true, false },
437 #else // FILE_PATH_USES_DRIVE_LETTERS
438 { FPL("/"), true, false },
439 { FPL("/a"), true, false },
440 { FPL("/."), true, false },
441 { FPL("/.."), true, false },
442 { FPL("c:/"), false, false },
443 #endif // FILE_PATH_USES_DRIVE_LETTERS
444 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
445 { FPL("a\\b"), false, false },
446 { FPL("\\\\"), true, true },
447 { FPL("\\\\a"), true, true },
448 { FPL("a\\b"), false, false },
449 { FPL("\\\\"), true, true },
450 { FPL("//a"), true, true },
451 { FPL("c:a\\b"), false, false },
452 { FPL("?:\\a"), false, false },
453 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
454 { FPL("\\"), false, false },
455 { FPL("\\a"), false, false },
456 { FPL("\\."), false, false },
457 { FPL("\\.."), false, false },
458 { FPL("c:\\"), true, false },
459 { FPL("c:\\"), true, false },
460 { FPL("c:\\a"), true, false },
461 { FPL("c:\\."), true, false },
462 { FPL("c:\\.."), true, false },
463 { FPL("C:\\a"), true, false },
464 { FPL("d:\\a"), true, false },
465 #else // FILE_PATH_USES_DRIVE_LETTERS
466 { FPL("\\"), true, false },
467 { FPL("\\a"), true, false },
468 { FPL("\\."), true, false },
469 { FPL("\\.."), true, false },
470 { FPL("c:\\"), false, false },
471 #endif // FILE_PATH_USES_DRIVE_LETTERS
472 #endif // FILE_PATH_USES_WIN_SEPARATORS
473 };
474
475 for (size_t i = 0; i < std::size(cases); ++i) {
476 FilePath input(cases[i].input);
477 bool observed_is_absolute = input.IsAbsolute();
478 EXPECT_EQ(cases[i].expected_is_absolute, observed_is_absolute) <<
479 "i: " << i << ", input: " << input.value();
480 bool observed_is_network = input.IsNetwork();
481 EXPECT_EQ(cases[i].expected_is_network, observed_is_network) <<
482 "i: " << i << ", input: " << input.value();
483 }
484 }
485
TEST_F(FilePathTest,PathComponentsTest)486 TEST_F(FilePathTest, PathComponentsTest) {
487 const struct UnaryTestData cases[] = {
488 { FPL("//foo/bar/baz/"), FPL("|//|foo|bar|baz")},
489 { FPL("///"), FPL("|/")},
490 #if BUILDFLAG(IS_POSIX)
491 {FPL("///foo//bar/baz"), FPL("|/|foo|bar|baz")},
492 #endif // BUILDFLAG(IS_POSIX)
493 { FPL("/foo//bar//baz/"), FPL("|/|foo|bar|baz")},
494 { FPL("/foo/bar/baz/"), FPL("|/|foo|bar|baz")},
495 { FPL("/foo/bar/baz//"), FPL("|/|foo|bar|baz")},
496 { FPL("/foo/bar/baz///"), FPL("|/|foo|bar|baz")},
497 { FPL("/foo/bar/baz"), FPL("|/|foo|bar|baz")},
498 { FPL("/foo/bar.bot/baz.txt"), FPL("|/|foo|bar.bot|baz.txt")},
499 { FPL("//foo//bar/baz"), FPL("|//|foo|bar|baz")},
500 { FPL("/"), FPL("|/")},
501 { FPL("foo"), FPL("|foo")},
502 { FPL(""), FPL("")},
503 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
504 { FPL("e:/foo"), FPL("|e:|/|foo")},
505 { FPL("e:/"), FPL("|e:|/")},
506 { FPL("e:"), FPL("|e:")},
507 #endif // FILE_PATH_USES_DRIVE_LETTERS
508 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
509 { FPL("../foo"), FPL("|..|foo")},
510 { FPL("./foo"), FPL("|foo")},
511 { FPL("../foo/bar/"), FPL("|..|foo|bar") },
512 { FPL("\\\\foo\\bar\\baz\\"), FPL("|\\\\|foo|bar|baz")},
513 { FPL("\\\\\\"), FPL("|\\")},
514 { FPL("\\foo\\\\bar\\\\baz\\"), FPL("|\\|foo|bar|baz")},
515 { FPL("\\foo\\bar\\baz\\"), FPL("|\\|foo|bar|baz")},
516 { FPL("\\foo\\bar\\baz\\\\"), FPL("|\\|foo|bar|baz")},
517 { FPL("\\foo\\bar\\baz\\\\\\"), FPL("|\\|foo|bar|baz")},
518 { FPL("\\foo\\bar\\baz"), FPL("|\\|foo|bar|baz")},
519 { FPL("\\foo\\bar/baz\\\\\\"), FPL("|\\|foo|bar|baz")},
520 { FPL("/foo\\bar\\baz"), FPL("|/|foo|bar|baz")},
521 { FPL("\\foo\\bar.bot\\baz.txt"), FPL("|\\|foo|bar.bot|baz.txt")},
522 { FPL("\\\\foo\\\\bar\\baz"), FPL("|\\\\|foo|bar|baz")},
523 { FPL("\\"), FPL("|\\")},
524 #endif // FILE_PATH_USES_WIN_SEPARATORS
525 };
526
527 for (size_t i = 0; i < std::size(cases); ++i) {
528 FilePath input(cases[i].input);
529 std::vector<FilePath::StringType> comps = input.GetComponents();
530
531 FilePath::StringType observed;
532 for (const auto& j : comps) {
533 observed.append(FILE_PATH_LITERAL("|"), 1);
534 observed.append(j);
535 }
536 EXPECT_EQ(FilePath::StringType(cases[i].expected), observed) <<
537 "i: " << i << ", input: " << input.value();
538 }
539 }
540
TEST_F(FilePathTest,IsParentTest)541 TEST_F(FilePathTest, IsParentTest) {
542 const struct BinaryBooleanTestData cases[] = {
543 { { FPL("/"), FPL("/foo/bar/baz") }, true},
544 { { FPL("/foo"), FPL("/foo/bar/baz") }, true},
545 { { FPL("/foo/bar"), FPL("/foo/bar/baz") }, true},
546 { { FPL("/foo/bar/"), FPL("/foo/bar/baz") }, true},
547 { { FPL("//foo/bar/"), FPL("//foo/bar/baz") }, true},
548 { { FPL("/foo/bar"), FPL("/foo2/bar/baz") }, false},
549 { { FPL("/foo/bar.txt"), FPL("/foo/bar/baz") }, false},
550 { { FPL("/foo/bar"), FPL("/foo/bar2/baz") }, false},
551 { { FPL("/foo/bar"), FPL("/foo/bar") }, false},
552 { { FPL("/foo/bar/baz"), FPL("/foo/bar") }, false},
553 { { FPL("foo"), FPL("foo/bar/baz") }, true},
554 { { FPL("foo/bar"), FPL("foo/bar/baz") }, true},
555 { { FPL("foo/bar"), FPL("foo2/bar/baz") }, false},
556 { { FPL("foo/bar"), FPL("foo/bar2/baz") }, false},
557 { { FPL(""), FPL("foo") }, false},
558 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
559 { { FPL("c:/foo/bar"), FPL("c:/foo/bar/baz") }, true},
560 { { FPL("E:/foo/bar"), FPL("e:/foo/bar/baz") }, true},
561 { { FPL("f:/foo/bar"), FPL("F:/foo/bar/baz") }, true},
562 { { FPL("E:/Foo/bar"), FPL("e:/foo/bar/baz") }, false},
563 { { FPL("f:/foo/bar"), FPL("F:/foo/Bar/baz") }, false},
564 { { FPL("c:/"), FPL("c:/foo/bar/baz") }, true},
565 { { FPL("c:"), FPL("c:/foo/bar/baz") }, true},
566 { { FPL("c:/foo/bar"), FPL("d:/foo/bar/baz") }, false},
567 { { FPL("c:/foo/bar"), FPL("D:/foo/bar/baz") }, false},
568 { { FPL("C:/foo/bar"), FPL("d:/foo/bar/baz") }, false},
569 { { FPL("c:/foo/bar"), FPL("c:/foo2/bar/baz") }, false},
570 { { FPL("e:/foo/bar"), FPL("E:/foo2/bar/baz") }, false},
571 { { FPL("F:/foo/bar"), FPL("f:/foo2/bar/baz") }, false},
572 { { FPL("c:/foo/bar"), FPL("c:/foo/bar2/baz") }, false},
573 #endif // FILE_PATH_USES_DRIVE_LETTERS
574 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
575 { { FPL("\\foo\\bar"), FPL("\\foo\\bar\\baz") }, true},
576 { { FPL("\\foo/bar"), FPL("\\foo\\bar\\baz") }, true},
577 { { FPL("\\foo/bar"), FPL("\\foo/bar/baz") }, true},
578 { { FPL("\\"), FPL("\\foo\\bar\\baz") }, true},
579 { { FPL(""), FPL("\\foo\\bar\\baz") }, false},
580 { { FPL("\\foo\\bar"), FPL("\\foo2\\bar\\baz") }, false},
581 { { FPL("\\foo\\bar"), FPL("\\foo\\bar2\\baz") }, false},
582 #endif // FILE_PATH_USES_WIN_SEPARATORS
583 };
584
585 for (size_t i = 0; i < std::size(cases); ++i) {
586 FilePath parent(cases[i].inputs[0]);
587 FilePath child(cases[i].inputs[1]);
588
589 EXPECT_EQ(parent.IsParent(child), cases[i].expected) <<
590 "i: " << i << ", parent: " << parent.value() << ", child: " <<
591 child.value();
592 }
593 }
594
TEST_F(FilePathTest,AppendRelativePathTest)595 TEST_F(FilePathTest, AppendRelativePathTest) {
596 const struct BinaryTestData cases[] = {
597 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
598 { { FPL("/"), FPL("/foo/bar/baz") }, FPL("foo\\bar\\baz")},
599 #else // FILE_PATH_USES_WIN_SEPARATORS
600 { { FPL("/"), FPL("/foo/bar/baz") }, FPL("foo/bar/baz")},
601 #endif // FILE_PATH_USES_WIN_SEPARATORS
602 { { FPL("/foo/bar"), FPL("/foo/bar/baz") }, FPL("baz")},
603 { { FPL("/foo/bar/"), FPL("/foo/bar/baz") }, FPL("baz")},
604 { { FPL("//foo/bar/"), FPL("//foo/bar/baz") }, FPL("baz")},
605 { { FPL("/foo/bar"), FPL("/foo2/bar/baz") }, FPL("")},
606 { { FPL("/foo/bar.txt"), FPL("/foo/bar/baz") }, FPL("")},
607 { { FPL("/foo/bar"), FPL("/foo/bar2/baz") }, FPL("")},
608 { { FPL("/foo/bar"), FPL("/foo/bar") }, FPL("")},
609 { { FPL("/foo/bar/baz"), FPL("/foo/bar") }, FPL("")},
610 { { FPL("foo/bar"), FPL("foo/bar/baz") }, FPL("baz")},
611 { { FPL("foo/bar"), FPL("foo2/bar/baz") }, FPL("")},
612 { { FPL("foo/bar"), FPL("foo/bar2/baz") }, FPL("")},
613 { { FPL(""), FPL("foo") }, FPL("")},
614 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
615 { { FPL("c:/foo/bar"), FPL("c:/foo/bar/baz") }, FPL("baz")},
616 { { FPL("E:/foo/bar"), FPL("e:/foo/bar/baz") }, FPL("baz")},
617 { { FPL("f:/foo/bar"), FPL("F:/foo/bar/baz") }, FPL("baz")},
618 { { FPL("E:/Foo/bar"), FPL("e:/foo/bar/baz") }, FPL("")},
619 { { FPL("f:/foo/bar"), FPL("F:/foo/Bar/baz") }, FPL("")},
620 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
621 { { FPL("c:/"), FPL("c:/foo/bar/baz") }, FPL("foo\\bar\\baz")},
622 // TODO(akalin): Figure out how to handle the corner case in the
623 // commented-out test case below. Appending to an empty path gives
624 // /foo\bar\baz but appending to a nonempty path "blah" gives
625 // blah\foo\bar\baz.
626 // { { FPL("c:"), FPL("c:/foo/bar/baz") }, FPL("foo\\bar\\baz")},
627 #endif // FILE_PATH_USES_WIN_SEPARATORS
628 { { FPL("c:/foo/bar"), FPL("d:/foo/bar/baz") }, FPL("")},
629 { { FPL("c:/foo/bar"), FPL("D:/foo/bar/baz") }, FPL("")},
630 { { FPL("C:/foo/bar"), FPL("d:/foo/bar/baz") }, FPL("")},
631 { { FPL("c:/foo/bar"), FPL("c:/foo2/bar/baz") }, FPL("")},
632 { { FPL("e:/foo/bar"), FPL("E:/foo2/bar/baz") }, FPL("")},
633 { { FPL("F:/foo/bar"), FPL("f:/foo2/bar/baz") }, FPL("")},
634 { { FPL("c:/foo/bar"), FPL("c:/foo/bar2/baz") }, FPL("")},
635 #endif // FILE_PATH_USES_DRIVE_LETTERS
636 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
637 { { FPL("\\foo\\bar"), FPL("\\foo\\bar\\baz") }, FPL("baz")},
638 { { FPL("\\foo/bar"), FPL("\\foo\\bar\\baz") }, FPL("baz")},
639 { { FPL("\\foo/bar"), FPL("\\foo/bar/baz") }, FPL("baz")},
640 { { FPL("\\"), FPL("\\foo\\bar\\baz") }, FPL("foo\\bar\\baz")},
641 { { FPL(""), FPL("\\foo\\bar\\baz") }, FPL("")},
642 { { FPL("\\foo\\bar"), FPL("\\foo2\\bar\\baz") }, FPL("")},
643 { { FPL("\\foo\\bar"), FPL("\\foo\\bar2\\baz") }, FPL("")},
644 #endif // FILE_PATH_USES_WIN_SEPARATORS
645
646 // For network paths, the hosts are compared ignoring case, while the rest
647 // of the path is compared using case.
648 { { FPL("//FOO/bar/"), FPL("//foo/bar/baz") }, FPL("baz")},
649 { { FPL("//foo/BAR/"), FPL("//foo/bar/baz") }, FPL("")},
650 // For non-network paths, the first component is not a host and should be
651 // compared using case.
652 { { FPL("/FOO/bar/"), FPL("/foo/bar/baz") }, FPL("")},
653 // Degenerate case when parent has no hostname.
654 { { FPL("//"), FPL("//foo") }, FPL("foo")},
655 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
656 // Network path tests but using Windows path separators.
657 { { FPL("\\\\FOO\\bar"), FPL("\\\\foo\\bar\\baz") }, FPL("baz")},
658 { { FPL("\\\\fOO\\Bar"), FPL("\\\\foo\\bar\\baz") }, FPL("")},
659 { { FPL("\\FOO\\bar"), FPL("\\foo\\bar\\baz") }, FPL("")},
660 { { FPL("\\\\"), FPL("\\\\foo") }, FPL("foo")},
661 #endif // FILE_PATH_USES_WIN_SEPARATORS
662
663 };
664
665 const FilePath base(FPL("blah"));
666
667 for (size_t i = 0; i < std::size(cases); ++i) {
668 FilePath parent(cases[i].inputs[0]);
669 FilePath child(cases[i].inputs[1]);
670 {
671 FilePath result;
672 bool success = parent.AppendRelativePath(child, &result);
673 EXPECT_EQ(!cases[i].expected.empty(), success)
674 << "i: " << i << ", parent: " << parent.value()
675 << ", child: " << child.value();
676 EXPECT_EQ(cases[i].expected, result.value())
677 << "i: " << i << ", parent: " << parent.value()
678 << ", child: " << child.value();
679 }
680 {
681 FilePath result(base);
682 bool success = parent.AppendRelativePath(child, &result);
683 EXPECT_EQ(!cases[i].expected.empty(), success)
684 << "i: " << i << ", parent: " << parent.value()
685 << ", child: " << child.value();
686 EXPECT_EQ(base.Append(cases[i].expected).value(), result.value()) <<
687 "i: " << i << ", parent: " << parent.value() << ", child: " <<
688 child.value();
689 }
690 }
691 }
692
TEST_F(FilePathTest,EqualityTest)693 TEST_F(FilePathTest, EqualityTest) {
694 const struct BinaryBooleanTestData cases[] = {
695 { { FPL("/foo/bar/baz"), FPL("/foo/bar/baz") }, true},
696 { { FPL("/foo/bar"), FPL("/foo/bar/baz") }, false},
697 { { FPL("/foo/bar/baz"), FPL("/foo/bar") }, false},
698 { { FPL("//foo/bar/"), FPL("//foo/bar/") }, true},
699 { { FPL("/foo/bar"), FPL("/foo2/bar") }, false},
700 { { FPL("/foo/bar.txt"), FPL("/foo/bar") }, false},
701 { { FPL("foo/bar"), FPL("foo/bar") }, true},
702 { { FPL("foo/bar"), FPL("foo/bar/baz") }, false},
703 { { FPL(""), FPL("foo") }, false},
704 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
705 { { FPL("c:/foo/bar"), FPL("c:/foo/bar") }, true},
706 { { FPL("E:/foo/bar"), FPL("e:/foo/bar") }, true},
707 { { FPL("f:/foo/bar"), FPL("F:/foo/bar") }, true},
708 { { FPL("E:/Foo/bar"), FPL("e:/foo/bar") }, false},
709 { { FPL("f:/foo/bar"), FPL("F:/foo/Bar") }, false},
710 { { FPL("c:/"), FPL("c:/") }, true},
711 { { FPL("c:"), FPL("c:") }, true},
712 { { FPL("c:/foo/bar"), FPL("d:/foo/bar") }, false},
713 { { FPL("c:/foo/bar"), FPL("D:/foo/bar") }, false},
714 { { FPL("C:/foo/bar"), FPL("d:/foo/bar") }, false},
715 { { FPL("c:/foo/bar"), FPL("c:/foo2/bar") }, false},
716 #endif // FILE_PATH_USES_DRIVE_LETTERS
717 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
718 { { FPL("\\foo\\bar"), FPL("\\foo\\bar") }, true},
719 { { FPL("\\foo/bar"), FPL("\\foo/bar") }, true},
720 { { FPL("\\foo/bar"), FPL("\\foo\\bar") }, false},
721 { { FPL("\\"), FPL("\\") }, true},
722 { { FPL("\\"), FPL("/") }, false},
723 { { FPL(""), FPL("\\") }, false},
724 { { FPL("\\foo\\bar"), FPL("\\foo2\\bar") }, false},
725 { { FPL("\\foo\\bar"), FPL("\\foo\\bar2") }, false},
726 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
727 { { FPL("c:\\foo\\bar"), FPL("c:\\foo\\bar") }, true},
728 { { FPL("E:\\foo\\bar"), FPL("e:\\foo\\bar") }, true},
729 { { FPL("f:\\foo\\bar"), FPL("F:\\foo/bar") }, false},
730 #endif // FILE_PATH_USES_DRIVE_LETTERS
731 #endif // FILE_PATH_USES_WIN_SEPARATORS
732 };
733
734 for (size_t i = 0; i < std::size(cases); ++i) {
735 FilePath a(cases[i].inputs[0]);
736 FilePath b(cases[i].inputs[1]);
737
738 EXPECT_EQ(a == b, cases[i].expected) <<
739 "equality i: " << i << ", a: " << a.value() << ", b: " <<
740 b.value();
741 }
742
743 for (size_t i = 0; i < std::size(cases); ++i) {
744 FilePath a(cases[i].inputs[0]);
745 FilePath b(cases[i].inputs[1]);
746
747 EXPECT_EQ(a != b, !cases[i].expected) <<
748 "inequality i: " << i << ", a: " << a.value() << ", b: " <<
749 b.value();
750 }
751 }
752
TEST_F(FilePathTest,MacroExpansion)753 TEST_F(FilePathTest, MacroExpansion) {
754 EXPECT_EQ(FILE_PATH_LITERAL(TEST_FILE), FILE_PATH_LITERAL("TestFile"));
755 }
756
TEST_F(FilePathTest,Extension)757 TEST_F(FilePathTest, Extension) {
758 FilePath base_dir(FILE_PATH_LITERAL("base_dir"));
759
760 FilePath jpg = base_dir.Append(FILE_PATH_LITERAL("foo.jpg"));
761 EXPECT_EQ(FILE_PATH_LITERAL(".jpg"), jpg.Extension());
762 EXPECT_EQ(FILE_PATH_LITERAL(".jpg"), jpg.FinalExtension());
763
764 FilePath base = jpg.BaseName().RemoveExtension();
765 EXPECT_EQ(FILE_PATH_LITERAL("foo"), base.value());
766
767 FilePath path_no_ext = base_dir.Append(base);
768 EXPECT_EQ(path_no_ext.value(), jpg.RemoveExtension().value());
769
770 EXPECT_EQ(path_no_ext.value(), path_no_ext.RemoveExtension().value());
771 EXPECT_EQ(FILE_PATH_LITERAL(""), path_no_ext.Extension());
772 EXPECT_EQ(FILE_PATH_LITERAL(""), path_no_ext.FinalExtension());
773 }
774
TEST_F(FilePathTest,Extension2)775 TEST_F(FilePathTest, Extension2) {
776 // clang-format off
777 const struct UnaryTestData cases[] = {
778 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
779 { FPL("C:\\a\\b\\c.ext"), FPL(".ext") },
780 { FPL("C:\\a\\b\\c."), FPL(".") },
781 { FPL("C:\\a\\b\\c"), FPL("") },
782 { FPL("C:\\a\\b\\"), FPL("") },
783 { FPL("C:\\a\\b.\\"), FPL(".") },
784 { FPL("C:\\a\\b\\c.ext1.ext2"), FPL(".ext2") },
785 { FPL("C:\\foo.bar\\\\\\"), FPL(".bar") },
786 { FPL("C:\\foo.bar\\.."), FPL("") },
787 { FPL("C:\\foo.bar\\..\\\\"), FPL("") },
788 #endif
789 { FPL("/foo/bar/baz.EXT"), FPL(".EXT") },
790 { FPL("/foo/bar/baz.Ext"), FPL(".Ext") },
791 { FPL("/foo/bar/baz.ext"), FPL(".ext") },
792 { FPL("/foo/bar/baz."), FPL(".") },
793 { FPL("/foo/bar/baz.."), FPL(".") },
794 { FPL("/foo/bar/baz"), FPL("") },
795 { FPL("/foo/bar/"), FPL("") },
796 { FPL("/foo/bar./"), FPL(".") },
797 { FPL("/foo/bar/baz.ext1.ext2"), FPL(".ext2") },
798 { FPL("/subversion-1.6.12.zip"), FPL(".zip") },
799 { FPL("/foo.12345.gz"), FPL(".gz") },
800 { FPL("/foo..gz"), FPL(".gz") },
801 { FPL("."), FPL("") },
802 { FPL(".."), FPL("") },
803 { FPL("./foo"), FPL("") },
804 { FPL("./foo.ext"), FPL(".ext") },
805 { FPL("/foo.ext1/bar.ext2"), FPL(".ext2") },
806 { FPL("/foo.bar////"), FPL(".bar") },
807 { FPL("/foo.bar/.."), FPL("") },
808 { FPL("/foo.bar/..////"), FPL("") },
809 { FPL("/foo.1234.luser.js"), FPL(".js") },
810 { FPL("/user.js"), FPL(".js") },
811 };
812 const struct UnaryTestData double_extension_cases[] = {
813 // `kCommonDoubleExtensionSuffixes` cases. Blah is not on that allow-list.
814 // Membership is (ASCII) case-insensitive: both ".Z" and ".z" match.
815 { FPL("/foo.TAR.bz2"), FPL(".TAR.bz2") },
816 { FPL("/foo.tar.Z"), FPL(".tar.Z") },
817 { FPL("/foo.tar.blah"), FPL(".blah") },
818 { FPL("/foo.tar.bz"), FPL(".tar.bz") },
819 { FPL("/foo.tar.bz2"), FPL(".tar.bz2") },
820 { FPL("/foo.tar.gz"), FPL(".tar.gz") },
821 { FPL("/foo.tar.lz"), FPL(".tar.lz") },
822 { FPL("/foo.tar.lzma"), FPL(".tar.lzma") },
823 { FPL("/foo.tar.lzo"), FPL(".tar.lzo") },
824 { FPL("/foo.tar.xz"), FPL(".tar.xz") },
825 { FPL("/foo.tar.z"), FPL(".tar.z") },
826 { FPL("/foo.tar.zst"), FPL(".tar.zst") },
827 // `kCommonDoubleExtensions` cases.
828 { FPL("/foo.1234.user.js"), FPL(".user.js") },
829 { FPL("foo.user.js"), FPL(".user.js") },
830 // Other cases.
831 { FPL("/foo.1234.gz"), FPL(".1234.gz") },
832 { FPL("/foo.1234.gz."), FPL(".") },
833 { FPL("/foo.1234.tar.gz"), FPL(".tar.gz") },
834 { FPL("/foo.tar.tar.gz"), FPL(".tar.gz") },
835 { FPL("/foo.tar.gz.gz"), FPL(".gz.gz") },
836 };
837 // clang-format on
838
839 for (size_t i = 0; i < std::size(cases); ++i) {
840 FilePath path(cases[i].input);
841 FilePath::StringType extension = path.Extension();
842 FilePath::StringType final_extension = path.FinalExtension();
843 EXPECT_EQ(cases[i].expected, extension)
844 << "i: " << i << ", path: " << path.value();
845 EXPECT_EQ(cases[i].expected, final_extension)
846 << "i: " << i << ", path: " << path.value();
847 }
848
849 for (size_t i = 0; i < std::size(double_extension_cases); ++i) {
850 FilePath path(double_extension_cases[i].input);
851 FilePath::StringType extension = path.Extension();
852 EXPECT_EQ(double_extension_cases[i].expected, extension)
853 << "i: " << i << ", path: " << path.value();
854 }
855 }
856
TEST_F(FilePathTest,InsertBeforeExtension)857 TEST_F(FilePathTest, InsertBeforeExtension) {
858 const struct BinaryTestData cases[] = {
859 { { FPL(""), FPL("") }, FPL("") },
860 { { FPL(""), FPL("txt") }, FPL("") },
861 { { FPL("."), FPL("txt") }, FPL("") },
862 { { FPL(".."), FPL("txt") }, FPL("") },
863 { { FPL("foo.dll"), FPL("txt") }, FPL("footxt.dll") },
864 { { FPL("."), FPL("") }, FPL(".") },
865 { { FPL("foo.dll"), FPL(".txt") }, FPL("foo.txt.dll") },
866 { { FPL("foo"), FPL("txt") }, FPL("footxt") },
867 { { FPL("foo"), FPL(".txt") }, FPL("foo.txt") },
868 { { FPL("foo.baz.dll"), FPL("txt") }, FPL("foo.baztxt.dll") },
869 { { FPL("foo.baz.dll"), FPL(".txt") }, FPL("foo.baz.txt.dll") },
870 { { FPL("foo.dll"), FPL("") }, FPL("foo.dll") },
871 { { FPL("foo.dll"), FPL(".") }, FPL("foo..dll") },
872 { { FPL("foo"), FPL("") }, FPL("foo") },
873 { { FPL("foo"), FPL(".") }, FPL("foo.") },
874 { { FPL("foo.baz.dll"), FPL("") }, FPL("foo.baz.dll") },
875 { { FPL("foo.baz.dll"), FPL(".") }, FPL("foo.baz..dll") },
876 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
877 { { FPL("\\"), FPL("") }, FPL("\\") },
878 { { FPL("\\"), FPL("txt") }, FPL("\\txt") },
879 { { FPL("\\."), FPL("txt") }, FPL("") },
880 { { FPL("\\.."), FPL("txt") }, FPL("") },
881 { { FPL("\\."), FPL("") }, FPL("\\.") },
882 { { FPL("C:\\bar\\foo.dll"), FPL("txt") },
883 FPL("C:\\bar\\footxt.dll") },
884 { { FPL("C:\\bar.baz\\foodll"), FPL("txt") },
885 FPL("C:\\bar.baz\\foodlltxt") },
886 { { FPL("C:\\bar.baz\\foo.dll"), FPL("txt") },
887 FPL("C:\\bar.baz\\footxt.dll") },
888 { { FPL("C:\\bar.baz\\foo.dll.exe"), FPL("txt") },
889 FPL("C:\\bar.baz\\foo.dlltxt.exe") },
890 { { FPL("C:\\bar.baz\\foo"), FPL("") },
891 FPL("C:\\bar.baz\\foo") },
892 { { FPL("C:\\bar.baz\\foo.exe"), FPL("") },
893 FPL("C:\\bar.baz\\foo.exe") },
894 { { FPL("C:\\bar.baz\\foo.dll.exe"), FPL("") },
895 FPL("C:\\bar.baz\\foo.dll.exe") },
896 { { FPL("C:\\bar\\baz\\foo.exe"), FPL(" (1)") },
897 FPL("C:\\bar\\baz\\foo (1).exe") },
898 { { FPL("C:\\foo.baz\\\\"), FPL(" (1)") }, FPL("C:\\foo (1).baz") },
899 { { FPL("C:\\foo.baz\\..\\"), FPL(" (1)") }, FPL("") },
900 #endif
901 { { FPL("/"), FPL("") }, FPL("/") },
902 { { FPL("/"), FPL("txt") }, FPL("/txt") },
903 { { FPL("/."), FPL("txt") }, FPL("") },
904 { { FPL("/.."), FPL("txt") }, FPL("") },
905 { { FPL("/."), FPL("") }, FPL("/.") },
906 { { FPL("/bar/foo.dll"), FPL("txt") }, FPL("/bar/footxt.dll") },
907 { { FPL("/bar.baz/foodll"), FPL("txt") }, FPL("/bar.baz/foodlltxt") },
908 { { FPL("/bar.baz/foo.dll"), FPL("txt") }, FPL("/bar.baz/footxt.dll") },
909 { { FPL("/bar.baz/foo.dll.exe"), FPL("txt") },
910 FPL("/bar.baz/foo.dlltxt.exe") },
911 { { FPL("/bar.baz/foo"), FPL("") }, FPL("/bar.baz/foo") },
912 { { FPL("/bar.baz/foo.exe"), FPL("") }, FPL("/bar.baz/foo.exe") },
913 { { FPL("/bar.baz/foo.dll.exe"), FPL("") }, FPL("/bar.baz/foo.dll.exe") },
914 { { FPL("/bar/baz/foo.exe"), FPL(" (1)") }, FPL("/bar/baz/foo (1).exe") },
915 { { FPL("/bar/baz/..////"), FPL(" (1)") }, FPL("") },
916 };
917 for (unsigned int i = 0; i < std::size(cases); ++i) {
918 FilePath path(cases[i].inputs[0]);
919 FilePath result = path.InsertBeforeExtension(cases[i].inputs[1]);
920 EXPECT_EQ(cases[i].expected, result.value())
921 << "i: " << i << ", path: " << path.value()
922 << ", insert: " << cases[i].inputs[1];
923 }
924 }
925
TEST_F(FilePathTest,RemoveExtension)926 TEST_F(FilePathTest, RemoveExtension) {
927 const struct UnaryTestData cases[] = {
928 { FPL(""), FPL("") },
929 { FPL("."), FPL(".") },
930 { FPL(".."), FPL("..") },
931 { FPL("foo.dll"), FPL("foo") },
932 { FPL("./foo.dll"), FPL("./foo") },
933 { FPL("foo..dll"), FPL("foo.") },
934 { FPL("foo"), FPL("foo") },
935 { FPL("foo."), FPL("foo") },
936 { FPL("foo.."), FPL("foo.") },
937 { FPL("foo.baz.dll"), FPL("foo.baz") },
938 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
939 { FPL("C:\\foo.bar\\foo"), FPL("C:\\foo.bar\\foo") },
940 { FPL("C:\\foo.bar\\..\\\\"), FPL("C:\\foo.bar\\..\\\\") },
941 #endif
942 { FPL("/foo.bar/foo"), FPL("/foo.bar/foo") },
943 { FPL("/foo.bar/..////"), FPL("/foo.bar/..////") },
944 };
945 for (size_t i = 0; i < std::size(cases); ++i) {
946 FilePath path(cases[i].input);
947 FilePath removed = path.RemoveExtension();
948 FilePath removed_final = path.RemoveFinalExtension();
949 EXPECT_EQ(cases[i].expected, removed.value()) << "i: " << i <<
950 ", path: " << path.value();
951 EXPECT_EQ(cases[i].expected, removed_final.value()) << "i: " << i <<
952 ", path: " << path.value();
953 }
954
955 const FilePath::StringPieceType tarballs[] = {
956 FPL("foo.tar.gz"), FPL("foo.tar.xz"), FPL("foo.tar.bz2"),
957 FPL("foo.tar.Z"), FPL("foo.tar.bz")};
958 for (size_t i = 0; i < std::size(tarballs); ++i) {
959 FilePath path(FPL("foo.tar.gz"));
960 FilePath removed = path.RemoveExtension();
961 FilePath removed_final = path.RemoveFinalExtension();
962 EXPECT_EQ(FPL("foo"), removed.value())
963 << "i: " << i << ", path: " << path.value();
964 EXPECT_EQ(FPL("foo.tar"), removed_final.value())
965 << "i: " << i << ", path: " << path.value();
966 }
967 }
968
TEST_F(FilePathTest,ReplaceExtension)969 TEST_F(FilePathTest, ReplaceExtension) {
970 const struct BinaryTestData cases[] = {
971 { { FPL(""), FPL("") }, FPL("") },
972 { { FPL(""), FPL("txt") }, FPL("") },
973 { { FPL("."), FPL("txt") }, FPL("") },
974 { { FPL(".."), FPL("txt") }, FPL("") },
975 { { FPL("."), FPL("") }, FPL("") },
976 { { FPL("foo.dll"), FPL("txt") }, FPL("foo.txt") },
977 { { FPL("./foo.dll"), FPL("txt") }, FPL("./foo.txt") },
978 { { FPL("foo..dll"), FPL("txt") }, FPL("foo..txt") },
979 { { FPL("foo.dll"), FPL(".txt") }, FPL("foo.txt") },
980 { { FPL("foo"), FPL("txt") }, FPL("foo.txt") },
981 { { FPL("foo."), FPL("txt") }, FPL("foo.txt") },
982 { { FPL("foo.."), FPL("txt") }, FPL("foo..txt") },
983 { { FPL("foo"), FPL(".txt") }, FPL("foo.txt") },
984 { { FPL("foo.baz.dll"), FPL("txt") }, FPL("foo.baz.txt") },
985 { { FPL("foo.baz.dll"), FPL(".txt") }, FPL("foo.baz.txt") },
986 { { FPL("foo.dll"), FPL("") }, FPL("foo") },
987 { { FPL("foo.dll"), FPL(".") }, FPL("foo") },
988 { { FPL("foo"), FPL("") }, FPL("foo") },
989 { { FPL("foo"), FPL(".") }, FPL("foo") },
990 { { FPL("foo.baz.dll"), FPL("") }, FPL("foo.baz") },
991 { { FPL("foo.baz.dll"), FPL(".") }, FPL("foo.baz") },
992 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
993 { { FPL("C:\\foo.bar\\foo"), FPL("baz") }, FPL("C:\\foo.bar\\foo.baz") },
994 { { FPL("C:\\foo.bar\\..\\\\"), FPL("baz") }, FPL("") },
995 #endif
996 { { FPL("/foo.bar/foo"), FPL("baz") }, FPL("/foo.bar/foo.baz") },
997 { { FPL("/foo.bar/..////"), FPL("baz") }, FPL("") },
998 };
999 for (unsigned int i = 0; i < std::size(cases); ++i) {
1000 FilePath path(cases[i].inputs[0]);
1001 FilePath replaced = path.ReplaceExtension(cases[i].inputs[1]);
1002 EXPECT_EQ(cases[i].expected, replaced.value()) << "i: " << i <<
1003 ", path: " << path.value() << ", replace: " << cases[i].inputs[1];
1004 }
1005 }
1006
TEST_F(FilePathTest,AddExtension)1007 TEST_F(FilePathTest, AddExtension) {
1008 const struct BinaryTestData cases[] = {
1009 { { FPL(""), FPL("") }, FPL("") },
1010 { { FPL(""), FPL("txt") }, FPL("") },
1011 { { FPL("."), FPL("txt") }, FPL("") },
1012 { { FPL(".."), FPL("txt") }, FPL("") },
1013 { { FPL("."), FPL("") }, FPL("") },
1014 { { FPL("foo.dll"), FPL("txt") }, FPL("foo.dll.txt") },
1015 { { FPL("./foo.dll"), FPL("txt") }, FPL("./foo.dll.txt") },
1016 { { FPL("foo..dll"), FPL("txt") }, FPL("foo..dll.txt") },
1017 { { FPL("foo.dll"), FPL(".txt") }, FPL("foo.dll.txt") },
1018 { { FPL("foo"), FPL("txt") }, FPL("foo.txt") },
1019 { { FPL("foo."), FPL("txt") }, FPL("foo.txt") },
1020 { { FPL("foo.."), FPL("txt") }, FPL("foo..txt") },
1021 { { FPL("foo"), FPL(".txt") }, FPL("foo.txt") },
1022 { { FPL("foo.baz.dll"), FPL("txt") }, FPL("foo.baz.dll.txt") },
1023 { { FPL("foo.baz.dll"), FPL(".txt") }, FPL("foo.baz.dll.txt") },
1024 { { FPL("foo.dll"), FPL("") }, FPL("foo.dll") },
1025 { { FPL("foo.dll"), FPL(".") }, FPL("foo.dll") },
1026 { { FPL("foo"), FPL("") }, FPL("foo") },
1027 { { FPL("foo"), FPL(".") }, FPL("foo") },
1028 { { FPL("foo.baz.dll"), FPL("") }, FPL("foo.baz.dll") },
1029 { { FPL("foo.baz.dll"), FPL(".") }, FPL("foo.baz.dll") },
1030 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
1031 { { FPL("C:\\foo.bar\\foo"), FPL("baz") }, FPL("C:\\foo.bar\\foo.baz") },
1032 { { FPL("C:\\foo.bar\\..\\\\"), FPL("baz") }, FPL("") },
1033 #endif
1034 { { FPL("/foo.bar/foo"), FPL("baz") }, FPL("/foo.bar/foo.baz") },
1035 { { FPL("/foo.bar/..////"), FPL("baz") }, FPL("") },
1036 };
1037 for (unsigned int i = 0; i < std::size(cases); ++i) {
1038 FilePath path(cases[i].inputs[0]);
1039 FilePath added = path.AddExtension(cases[i].inputs[1]);
1040 EXPECT_EQ(cases[i].expected, added.value()) << "i: " << i <<
1041 ", path: " << path.value() << ", add: " << cases[i].inputs[1];
1042 }
1043 }
1044
TEST_F(FilePathTest,MatchesExtension)1045 TEST_F(FilePathTest, MatchesExtension) {
1046 const struct BinaryBooleanTestData cases[] = {
1047 {{FPL("foo"), FPL("")}, true},
1048 {{FPL("foo"), FPL(".")}, false},
1049 {{FPL("foo."), FPL("")}, false},
1050 {{FPL("foo."), FPL(".")}, true},
1051 {{FPL("foo.txt"), FPL(".dll")}, false},
1052 {{FPL("foo.txt"), FPL(".txt")}, true},
1053 {{FPL("foo.txt.dll"), FPL(".txt")}, false},
1054 {{FPL("foo.txt.dll"), FPL(".dll")}, true},
1055 {{FPL("foo.tar.gz"), FPL(".gz")}, false},
1056 {{FPL("foo.tar.lzma"), FPL(".tar.lzma")}, true},
1057 {{FPL("foo.TXT"), FPL(".txt")}, true},
1058 {{FPL("foo.txt"), FPL(".TXT")}, true},
1059 {{FPL("foo.tXt"), FPL(".txt")}, true},
1060 {{FPL("foo.txt"), FPL(".tXt")}, true},
1061 {{FPL("foo.tXt"), FPL(".TXT")}, true},
1062 {{FPL("foo.tXt"), FPL(".tXt")}, true},
1063 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
1064 {{FPL("c:/foo.txt.dll"), FPL(".txt")}, false},
1065 {{FPL("c:/foo.txt"), FPL(".txt")}, true},
1066 #endif // FILE_PATH_USES_DRIVE_LETTERS
1067 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
1068 {{FPL("c:\\bar\\foo.txt.dll"), FPL(".txt")}, false},
1069 {{FPL("c:\\bar\\foo.txt"), FPL(".txt")}, true},
1070 #endif // FILE_PATH_USES_DRIVE_LETTERS
1071 {{FPL("/bar/foo.txt.dll"), FPL(".txt")}, false},
1072 {{FPL("/bar/foo.txt"), FPL(".txt")}, true},
1073 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE)
1074 // Umlauts A, O, U: direct comparison, and upper case vs. lower case
1075 {{FPL("foo.\u00E4\u00F6\u00FC"), FPL(".\u00E4\u00F6\u00FC")}, true},
1076 {{FPL("foo.\u00C4\u00D6\u00DC"), FPL(".\u00E4\u00F6\u00FC")}, true},
1077 // C with circumflex: direct comparison, and upper case vs. lower case
1078 {{FPL("foo.\u0109"), FPL(".\u0109")}, true},
1079 {{FPL("foo.\u0108"), FPL(".\u0109")}, true},
1080 #endif
1081 };
1082
1083 for (size_t i = 0; i < std::size(cases); ++i) {
1084 FilePath path(cases[i].inputs[0]);
1085 FilePath::StringType ext(cases[i].inputs[1]);
1086
1087 EXPECT_EQ(cases[i].expected, path.MatchesExtension(ext)) <<
1088 "i: " << i << ", path: " << path.value() << ", ext: " << ext;
1089 }
1090 }
1091
TEST_F(FilePathTest,MatchesFinalExtension)1092 TEST_F(FilePathTest, MatchesFinalExtension) {
1093 const struct BinaryBooleanTestData cases[] = {
1094 {{FPL("foo"), FPL("")}, true},
1095 {{FPL("foo"), FPL(".")}, false},
1096 {{FPL("foo."), FPL("")}, false},
1097 {{FPL("foo."), FPL(".")}, true},
1098 {{FPL("foo.txt"), FPL(".dll")}, false},
1099 {{FPL("foo.txt"), FPL(".txt")}, true},
1100 {{FPL("foo.txt.dll"), FPL(".txt")}, false},
1101 {{FPL("foo.txt.dll"), FPL(".dll")}, true},
1102 {{FPL("foo.tar.gz"), FPL(".gz")}, true},
1103 {{FPL("foo.tar.lzma"), FPL(".lzma")}, true},
1104 {{FPL("foo.tar.lzma"), FPL(".tar.lzma")}, false},
1105 {{FPL("foo.tlzma"), FPL(".tlzma")}, true},
1106 {{FPL("foo.TXT"), FPL(".txt")}, true},
1107 {{FPL("foo.txt"), FPL(".TXT")}, true},
1108 {{FPL("foo.tXt"), FPL(".txt")}, true},
1109 {{FPL("foo.txt"), FPL(".tXt")}, true},
1110 {{FPL("foo.tXt"), FPL(".TXT")}, true},
1111 {{FPL("foo.tXt"), FPL(".tXt")}, true},
1112 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
1113 {{FPL("c:/foo.txt.dll"), FPL(".txt")}, false},
1114 {{FPL("c:/foo.txt"), FPL(".txt")}, true},
1115 #endif // FILE_PATH_USES_DRIVE_LETTERS
1116 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
1117 {{FPL("c:\\bar\\foo.txt.dll"), FPL(".txt")}, false},
1118 {{FPL("c:\\bar\\foo.txt"), FPL(".txt")}, true},
1119 #endif // FILE_PATH_USES_DRIVE_LETTERS
1120 {{FPL("/bar/foo.txt.dll"), FPL(".txt")}, false},
1121 {{FPL("/bar/foo.txt"), FPL(".txt")}, true},
1122 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE)
1123 // Umlauts A, O, U: direct comparison, and upper case vs. lower case
1124 {{FPL("foo.\u00E4\u00F6\u00FC"), FPL(".\u00E4\u00F6\u00FC")}, true},
1125 {{FPL("foo.\u00C4\u00D6\u00DC"), FPL(".\u00E4\u00F6\u00FC")}, true},
1126 // C with circumflex: direct comparison, and upper case vs. lower case
1127 {{FPL("foo.\u0109"), FPL(".\u0109")}, true},
1128 {{FPL("foo.\u0108"), FPL(".\u0109")}, true},
1129 #endif
1130 };
1131
1132 for (size_t i = 0; i < std::size(cases); ++i) {
1133 FilePath path(cases[i].inputs[0]);
1134 FilePath::StringType ext(cases[i].inputs[1]);
1135
1136 EXPECT_EQ(cases[i].expected, path.MatchesFinalExtension(ext))
1137 << "i: " << i << ", path: " << path.value() << ", ext: " << ext;
1138 }
1139 }
1140
TEST_F(FilePathTest,CompareIgnoreCase)1141 TEST_F(FilePathTest, CompareIgnoreCase) {
1142 const struct BinaryIntTestData cases[] = {
1143 {{FPL("foo"), FPL("foo")}, 0},
1144 {{FPL("FOO"), FPL("foo")}, 0},
1145 {{FPL("foo.ext"), FPL("foo.ext")}, 0},
1146 {{FPL("FOO.EXT"), FPL("foo.ext")}, 0},
1147 {{FPL("Foo.Ext"), FPL("foo.ext")}, 0},
1148 {{FPL("foO"), FPL("foo")}, 0},
1149 {{FPL("foo"), FPL("foO")}, 0},
1150 {{FPL("fOo"), FPL("foo")}, 0},
1151 {{FPL("foo"), FPL("fOo")}, 0},
1152 {{FPL("bar"), FPL("foo")}, -1},
1153 {{FPL("foo"), FPL("bar")}, 1},
1154 {{FPL("BAR"), FPL("foo")}, -1},
1155 {{FPL("FOO"), FPL("bar")}, 1},
1156 {{FPL("bar"), FPL("FOO")}, -1},
1157 {{FPL("foo"), FPL("BAR")}, 1},
1158 {{FPL("BAR"), FPL("FOO")}, -1},
1159 {{FPL("FOO"), FPL("BAR")}, 1},
1160 // German "Eszett" (lower case and the new-fangled upper case)
1161 // Note that uc(<lowercase eszett>) => "SS", NOT <uppercase eszett>!
1162 // However, neither Windows nor Mac OSX converts these.
1163 // (or even have glyphs for <uppercase eszett>)
1164 {{FPL("\u00DF"), FPL("\u00DF")}, 0},
1165 {{FPL("\u1E9E"), FPL("\u1E9E")}, 0},
1166 {{FPL("\u00DF"), FPL("\u1E9E")}, -1},
1167 {{FPL("SS"), FPL("\u00DF")}, -1},
1168 {{FPL("SS"), FPL("\u1E9E")}, -1},
1169 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE)
1170 // Umlauts A, O, U: direct comparison, and upper case vs. lower case
1171 {{FPL("\u00E4\u00F6\u00FC"), FPL("\u00E4\u00F6\u00FC")}, 0},
1172 {{FPL("\u00C4\u00D6\u00DC"), FPL("\u00E4\u00F6\u00FC")}, 0},
1173 // C with circumflex: direct comparison, and upper case vs. lower case
1174 {{FPL("\u0109"), FPL("\u0109")}, 0},
1175 {{FPL("\u0108"), FPL("\u0109")}, 0},
1176 // Cyrillic letter SHA: direct comparison, and upper case vs. lower case
1177 {{FPL("\u0428"), FPL("\u0428")}, 0},
1178 {{FPL("\u0428"), FPL("\u0448")}, 0},
1179 // Greek letter DELTA: direct comparison, and upper case vs. lower case
1180 {{FPL("\u0394"), FPL("\u0394")}, 0},
1181 {{FPL("\u0394"), FPL("\u03B4")}, 0},
1182 // Japanese full-width A: direct comparison, and upper case vs. lower case
1183 // Note that full-width and standard characters are considered different.
1184 {{FPL("\uFF21"), FPL("\uFF21")}, 0},
1185 {{FPL("\uFF21"), FPL("\uFF41")}, 0},
1186 {{FPL("A"), FPL("\uFF21")}, -1},
1187 {{FPL("A"), FPL("\uFF41")}, -1},
1188 {{FPL("a"), FPL("\uFF21")}, -1},
1189 {{FPL("a"), FPL("\uFF41")}, -1},
1190 #endif
1191 #if BUILDFLAG(IS_APPLE)
1192 // Codepoints > 0x1000
1193 // Georgian letter DON: direct comparison, and upper case vs. lower case
1194 {{FPL("\u10A3"), FPL("\u10A3")}, 0},
1195 {{FPL("\u10A3"), FPL("\u10D3")}, 0},
1196 // Combining characters vs. pre-composed characters, upper and lower case
1197 {{FPL("k\u0301u\u032Do\u0304\u0301n"), FPL("\u1E31\u1E77\u1E53n")}, 0},
1198 {{FPL("k\u0301u\u032Do\u0304\u0301n"), FPL("kuon")}, 1},
1199 {{FPL("kuon"), FPL("k\u0301u\u032Do\u0304\u0301n")}, -1},
1200 {{FPL("K\u0301U\u032DO\u0304\u0301N"), FPL("KUON")}, 1},
1201 {{FPL("KUON"), FPL("K\u0301U\u032DO\u0304\u0301N")}, -1},
1202 {{FPL("k\u0301u\u032Do\u0304\u0301n"), FPL("KUON")}, 1},
1203 {{FPL("K\u0301U\u032DO\u0304\u0301N"), FPL("\u1E31\u1E77\u1E53n")}, 0},
1204 {{FPL("k\u0301u\u032Do\u0304\u0301n"), FPL("\u1E30\u1E76\u1E52n")}, 0},
1205 {{FPL("k\u0301u\u032Do\u0304\u0302n"), FPL("\u1E30\u1E76\u1E52n")}, 1},
1206
1207 // Codepoints > 0xFFFF
1208 // Here, we compare the `Adlam Letter Shu` in its capital and small version.
1209 {{FPL("\U0001E921"), FPL("\U0001E943")}, -1},
1210 {{FPL("\U0001E943"), FPL("\U0001E921")}, 1},
1211 {{FPL("\U0001E921"), FPL("\U0001E921")}, 0},
1212 {{FPL("\U0001E943"), FPL("\U0001E943")}, 0},
1213 #endif
1214 };
1215
1216 for (size_t i = 0; i < std::size(cases); ++i) {
1217 FilePath::StringType s1(cases[i].inputs[0]);
1218 FilePath::StringType s2(cases[i].inputs[1]);
1219 int result = FilePath::CompareIgnoreCase(s1, s2);
1220 EXPECT_EQ(cases[i].expected, result) <<
1221 "i: " << i << ", s1: " << s1 << ", s2: " << s2;
1222 }
1223 }
1224
TEST_F(FilePathTest,ReferencesParent)1225 TEST_F(FilePathTest, ReferencesParent) {
1226 const struct UnaryBooleanTestData cases[] = {
1227 { FPL("."), false },
1228 { FPL(".."), true },
1229 { FPL(".. "), true },
1230 { FPL(" .."), true },
1231 { FPL("..."), true },
1232 { FPL("a.."), false },
1233 { FPL("..a"), false },
1234 { FPL("../"), true },
1235 { FPL("/.."), true },
1236 { FPL("/../"), true },
1237 { FPL("/a../"), false },
1238 { FPL("/..a/"), false },
1239 { FPL("//.."), true },
1240 { FPL("..//"), true },
1241 { FPL("//..//"), true },
1242 { FPL("a//..//c"), true },
1243 { FPL("../b/c"), true },
1244 { FPL("/../b/c"), true },
1245 { FPL("a/b/.."), true },
1246 { FPL("a/b/../"), true },
1247 { FPL("a/../c"), true },
1248 { FPL("a/b/c"), false },
1249 };
1250
1251 for (size_t i = 0; i < std::size(cases); ++i) {
1252 FilePath input(cases[i].input);
1253 bool observed = input.ReferencesParent();
1254 EXPECT_EQ(cases[i].expected, observed) <<
1255 "i: " << i << ", input: " << input.value();
1256 }
1257 }
1258
TEST_F(FilePathTest,FromASCII)1259 TEST_F(FilePathTest, FromASCII) {
1260 const struct UTF8TestData cases[] = {
1261 {FPL("foo.txt"), "foo.txt"},
1262 {FPL("!#$%&'()"), "!#$%&'()"},
1263 };
1264
1265 for (size_t i = 0; i < std::size(cases); ++i) {
1266 FilePath from_ascii = FilePath::FromASCII(cases[i].utf8);
1267 EXPECT_EQ(FilePath::StringType(cases[i].native), from_ascii.value())
1268 << "i: " << i << ", input: " << cases[i].utf8;
1269 }
1270 }
1271
TEST_F(FilePathTest,FromUTF8Unsafe_And_AsUTF8Unsafe)1272 TEST_F(FilePathTest, FromUTF8Unsafe_And_AsUTF8Unsafe) {
1273 const struct UTF8TestData cases[] = {
1274 { FPL("foo.txt"), "foo.txt" },
1275 // "aeo" with accents. Use http://0xcc.net/jsescape/ to decode them.
1276 { FPL("\u00E0\u00E8\u00F2.txt"), "\xC3\xA0\xC3\xA8\xC3\xB2.txt" },
1277 // Full-width "ABC".
1278 { FPL("\uFF21\uFF22\uFF23.txt"),
1279 "\xEF\xBC\xA1\xEF\xBC\xA2\xEF\xBC\xA3.txt" },
1280 };
1281
1282 #if !defined(SYSTEM_NATIVE_UTF8) && \
1283 (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
1284 ScopedLocale locale("en_US.UTF-8");
1285 #endif
1286
1287 for (size_t i = 0; i < std::size(cases); ++i) {
1288 // Test FromUTF8Unsafe() works.
1289 FilePath from_utf8 = FilePath::FromUTF8Unsafe(cases[i].utf8);
1290 EXPECT_EQ(cases[i].native, from_utf8.value())
1291 << "i: " << i << ", input: " << cases[i].native;
1292 // Test AsUTF8Unsafe() works.
1293 FilePath from_native = FilePath(cases[i].native);
1294 EXPECT_EQ(cases[i].utf8, from_native.AsUTF8Unsafe())
1295 << "i: " << i << ", input: " << cases[i].native;
1296 // Test the two file paths are identical.
1297 EXPECT_EQ(from_utf8.value(), from_native.value());
1298 }
1299 }
1300
TEST_F(FilePathTest,ConstructWithNUL)1301 TEST_F(FilePathTest, ConstructWithNUL) {
1302 // Assert FPS() works.
1303 ASSERT_EQ(3U, FPS("a\0b").length());
1304
1305 // Test constructor strips '\0'
1306 FilePath path(FPS("a\0b"));
1307 EXPECT_EQ(1U, path.value().length());
1308 EXPECT_EQ(FPL("a"), path.value());
1309 }
1310
TEST_F(FilePathTest,AppendWithNUL)1311 TEST_F(FilePathTest, AppendWithNUL) {
1312 // Assert FPS() works.
1313 ASSERT_EQ(3U, FPS("b\0b").length());
1314
1315 // Test Append() strips '\0'
1316 FilePath path(FPL("a"));
1317 path = path.Append(FPS("b\0b"));
1318 EXPECT_EQ(3U, path.value().length());
1319 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
1320 EXPECT_EQ(FPL("a\\b"), path.value());
1321 #else
1322 EXPECT_EQ(FPL("a/b"), path.value());
1323 #endif
1324 }
1325
TEST_F(FilePathTest,AppendBaseName)1326 TEST_F(FilePathTest, AppendBaseName) {
1327 FilePath dir(FPL("foo"));
1328 auto file(SafeBaseName::Create(FPL("bar.txt")));
1329 EXPECT_TRUE(file);
1330
1331 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
1332 EXPECT_EQ(dir.Append(*file), FilePath(FPL("foo\\bar.txt")));
1333 #else
1334 EXPECT_EQ(dir.Append(*file), FilePath(FPL("foo/bar.txt")));
1335 #endif
1336 }
1337
TEST_F(FilePathTest,ReferencesParentWithNUL)1338 TEST_F(FilePathTest, ReferencesParentWithNUL) {
1339 // Assert FPS() works.
1340 ASSERT_EQ(3U, FPS("..\0").length());
1341
1342 // Test ReferencesParent() doesn't break with "..\0"
1343 FilePath path(FPS("..\0"));
1344 EXPECT_TRUE(path.ReferencesParent());
1345 }
1346
1347 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
TEST_F(FilePathTest,NormalizePathSeparators)1348 TEST_F(FilePathTest, NormalizePathSeparators) {
1349 const struct UnaryTestData cases[] = {
1350 { FPL("foo/bar"), FPL("foo\\bar") },
1351 { FPL("foo/bar\\betz"), FPL("foo\\bar\\betz") },
1352 { FPL("foo\\bar"), FPL("foo\\bar") },
1353 { FPL("foo\\bar/betz"), FPL("foo\\bar\\betz") },
1354 { FPL("foo"), FPL("foo") },
1355 // Trailing slashes don't automatically get stripped. That's what
1356 // StripTrailingSeparators() is for.
1357 { FPL("foo\\"), FPL("foo\\") },
1358 { FPL("foo/"), FPL("foo\\") },
1359 { FPL("foo/bar\\"), FPL("foo\\bar\\") },
1360 { FPL("foo\\bar/"), FPL("foo\\bar\\") },
1361 { FPL("foo/bar/"), FPL("foo\\bar\\") },
1362 { FPL("foo\\bar\\"), FPL("foo\\bar\\") },
1363 { FPL("\\foo/bar"), FPL("\\foo\\bar") },
1364 { FPL("/foo\\bar"), FPL("\\foo\\bar") },
1365 { FPL("c:/foo/bar/"), FPL("c:\\foo\\bar\\") },
1366 { FPL("/foo/bar/"), FPL("\\foo\\bar\\") },
1367 { FPL("\\foo\\bar\\"), FPL("\\foo\\bar\\") },
1368 { FPL("c:\\foo/bar"), FPL("c:\\foo\\bar") },
1369 { FPL("//foo\\bar\\"), FPL("\\\\foo\\bar\\") },
1370 { FPL("\\\\foo\\bar\\"), FPL("\\\\foo\\bar\\") },
1371 { FPL("//foo\\bar\\"), FPL("\\\\foo\\bar\\") },
1372 // This method does not normalize the number of path separators.
1373 { FPL("foo\\\\bar"), FPL("foo\\\\bar") },
1374 { FPL("foo//bar"), FPL("foo\\\\bar") },
1375 { FPL("foo/\\bar"), FPL("foo\\\\bar") },
1376 { FPL("foo\\/bar"), FPL("foo\\\\bar") },
1377 { FPL("///foo\\\\bar"), FPL("\\\\\\foo\\\\bar") },
1378 { FPL("foo//bar///"), FPL("foo\\\\bar\\\\\\") },
1379 { FPL("foo/\\bar/\\"), FPL("foo\\\\bar\\\\") },
1380 { FPL("/\\foo\\/bar"), FPL("\\\\foo\\\\bar") },
1381 };
1382 for (size_t i = 0; i < std::size(cases); ++i) {
1383 FilePath input(cases[i].input);
1384 FilePath observed = input.NormalizePathSeparators();
1385 EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) <<
1386 "i: " << i << ", input: " << input.value();
1387 }
1388 }
1389 #endif
1390
TEST_F(FilePathTest,EndsWithSeparator)1391 TEST_F(FilePathTest, EndsWithSeparator) {
1392 const UnaryBooleanTestData cases[] = {
1393 { FPL(""), false },
1394 { FPL("/"), true },
1395 { FPL("foo/"), true },
1396 { FPL("bar"), false },
1397 { FPL("/foo/bar"), false },
1398 };
1399 for (const auto& i : cases) {
1400 FilePath input = FilePath(i.input).NormalizePathSeparators();
1401 EXPECT_EQ(i.expected, input.EndsWithSeparator());
1402 }
1403 }
1404
TEST_F(FilePathTest,AsEndingWithSeparator)1405 TEST_F(FilePathTest, AsEndingWithSeparator) {
1406 const UnaryTestData cases[] = {
1407 { FPL(""), FPL("") },
1408 { FPL("/"), FPL("/") },
1409 { FPL("foo"), FPL("foo/") },
1410 { FPL("foo/"), FPL("foo/") }
1411 };
1412 for (const auto& i : cases) {
1413 FilePath input = FilePath(i.input).NormalizePathSeparators();
1414 FilePath expected = FilePath(i.expected).NormalizePathSeparators();
1415 EXPECT_EQ(expected.value(), input.AsEndingWithSeparator().value());
1416 }
1417 }
1418
1419 #if BUILDFLAG(IS_ANDROID)
TEST_F(FilePathTest,ContentUriTest)1420 TEST_F(FilePathTest, ContentUriTest) {
1421 const struct UnaryBooleanTestData cases[] = {
1422 { FPL("content://foo.bar"), true },
1423 { FPL("content://foo.bar/"), true },
1424 { FPL("content://foo/bar"), true },
1425 { FPL("CoNTenT://foo.bar"), true },
1426 { FPL("content://"), true },
1427 { FPL("content:///foo.bar"), true },
1428 { FPL("content://3foo/bar"), true },
1429 { FPL("content://_foo/bar"), true },
1430 { FPL(".. "), false },
1431 { FPL("foo.bar"), false },
1432 { FPL("content:foo.bar"), false },
1433 { FPL("content:/foo.ba"), false },
1434 { FPL("content:/dir/foo.bar"), false },
1435 { FPL("content: //foo.bar"), false },
1436 { FPL("content%2a%2f%2f"), false },
1437 };
1438
1439 for (size_t i = 0; i < std::size(cases); ++i) {
1440 FilePath input(cases[i].input);
1441 bool observed = input.IsContentUri();
1442 EXPECT_EQ(cases[i].expected, observed) <<
1443 "i: " << i << ", input: " << input.value();
1444 }
1445 }
1446 #endif
1447
1448 // Test the operator<<(ostream, FilePath).
TEST_F(FilePathTest,PrintToOstream)1449 TEST_F(FilePathTest, PrintToOstream) {
1450 std::stringstream ss;
1451 FilePath fp(FPL("foo"));
1452 ss << fp;
1453 EXPECT_EQ("foo", ss.str());
1454 }
1455
1456 #if BUILDFLAG(ENABLE_BASE_TRACING)
TEST_F(FilePathTest,TracedValueSupport)1457 TEST_F(FilePathTest, TracedValueSupport) {
1458 EXPECT_EQ(perfetto::TracedValueToString(FilePath(FPL("foo"))), "foo");
1459 }
1460 #endif // BUILDFLAG(ENABLE_BASE_TRACING)
1461
1462 // Test GetHFSDecomposedForm should return empty result for invalid UTF-8
1463 // strings.
1464 #if BUILDFLAG(IS_APPLE)
TEST_F(FilePathTest,GetHFSDecomposedFormWithInvalidInput)1465 TEST_F(FilePathTest, GetHFSDecomposedFormWithInvalidInput) {
1466 const FilePath::CharType* cases[] = {
1467 FPL("\xc3\x28"),
1468 FPL("\xe2\x82\x28"),
1469 FPL("\xe2\x28\xa1"),
1470 FPL("\xf0\x28\x8c\xbc"),
1471 FPL("\xf0\x28\x8c\x28"),
1472 };
1473 for (auto* invalid_input : cases) {
1474 FilePath::StringType observed = FilePath::GetHFSDecomposedForm(
1475 invalid_input);
1476 EXPECT_TRUE(observed.empty());
1477 }
1478 }
1479
TEST_F(FilePathTest,CompareIgnoreCaseWithInvalidInput)1480 TEST_F(FilePathTest, CompareIgnoreCaseWithInvalidInput) {
1481 const FilePath::CharType* cases[] = {
1482 FPL("\xc3\x28"), FPL("\xe2\x82\x28"), FPL("\xe2\x28\xa1"),
1483 FPL("\xf0\x28\x8c\xbc"), FPL("\xf0\x28\x8c\x28"),
1484 };
1485 for (auto* invalid_input : cases) {
1486 // All example inputs will be greater than the string "fixed".
1487 EXPECT_EQ(FilePath::CompareIgnoreCase(invalid_input, FPL("fixed")), 1);
1488 }
1489 }
1490 #endif
1491
1492 } // namespace base
1493