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