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