xref: /aosp_15_r20/external/cronet/base/files/file_path_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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