• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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