1 //===-- FileSpecTest.cpp ----------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "gtest/gtest.h"
11 
12 #include "lldb/Utility/FileSpec.h"
13 
14 using namespace lldb_private;
15 
16 TEST(FileSpecTest, FileAndDirectoryComponents) {
17   FileSpec fs_posix("/foo/bar", FileSpec::Style::posix);
18   EXPECT_STREQ("/foo/bar", fs_posix.GetCString());
19   EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString());
20   EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString());
21 
22   FileSpec fs_windows("F:\\bar", FileSpec::Style::windows);
23   EXPECT_STREQ("F:\\bar", fs_windows.GetCString());
24   // EXPECT_STREQ("F:\\", fs_windows.GetDirectory().GetCString()); // It returns
25   // "F:/"
26   EXPECT_STREQ("bar", fs_windows.GetFilename().GetCString());
27 
28   FileSpec fs_posix_root("/", FileSpec::Style::posix);
29   EXPECT_STREQ("/", fs_posix_root.GetCString());
30   EXPECT_EQ(nullptr, fs_posix_root.GetDirectory().GetCString());
31   EXPECT_STREQ("/", fs_posix_root.GetFilename().GetCString());
32 
33   FileSpec fs_net_drive("//net", FileSpec::Style::posix);
34   EXPECT_STREQ("//net", fs_net_drive.GetCString());
35   EXPECT_EQ(nullptr, fs_net_drive.GetDirectory().GetCString());
36   EXPECT_STREQ("//net", fs_net_drive.GetFilename().GetCString());
37 
38   FileSpec fs_net_root("//net/", FileSpec::Style::posix);
39   EXPECT_STREQ("//net/", fs_net_root.GetCString());
40   EXPECT_STREQ("//net", fs_net_root.GetDirectory().GetCString());
41   EXPECT_STREQ("/", fs_net_root.GetFilename().GetCString());
42 
43   FileSpec fs_windows_drive("F:", FileSpec::Style::windows);
44   EXPECT_STREQ("F:", fs_windows_drive.GetCString());
45   EXPECT_EQ(nullptr, fs_windows_drive.GetDirectory().GetCString());
46   EXPECT_STREQ("F:", fs_windows_drive.GetFilename().GetCString());
47 
48   FileSpec fs_windows_root("F:\\", FileSpec::Style::windows);
49   EXPECT_STREQ("F:\\", fs_windows_root.GetCString());
50   EXPECT_STREQ("F:", fs_windows_root.GetDirectory().GetCString());
51   // EXPECT_STREQ("\\", fs_windows_root.GetFilename().GetCString()); // It
52   // returns "/"
53 
54   FileSpec fs_posix_long("/foo/bar/baz", FileSpec::Style::posix);
55   EXPECT_STREQ("/foo/bar/baz", fs_posix_long.GetCString());
56   EXPECT_STREQ("/foo/bar", fs_posix_long.GetDirectory().GetCString());
57   EXPECT_STREQ("baz", fs_posix_long.GetFilename().GetCString());
58 
59   FileSpec fs_windows_long("F:\\bar\\baz", FileSpec::Style::windows);
60   EXPECT_STREQ("F:\\bar\\baz", fs_windows_long.GetCString());
61   // EXPECT_STREQ("F:\\bar", fs_windows_long.GetDirectory().GetCString()); // It
62   // returns "F:/bar"
63   EXPECT_STREQ("baz", fs_windows_long.GetFilename().GetCString());
64 
65   FileSpec fs_posix_trailing_slash("/foo/bar/", FileSpec::Style::posix);
66   EXPECT_STREQ("/foo/bar", fs_posix_trailing_slash.GetCString());
67   EXPECT_STREQ("/foo", fs_posix_trailing_slash.GetDirectory().GetCString());
68   EXPECT_STREQ("bar", fs_posix_trailing_slash.GetFilename().GetCString());
69 
70   FileSpec fs_windows_trailing_slash("F:\\bar\\", FileSpec::Style::windows);
71   EXPECT_STREQ("F:\\bar", fs_windows_trailing_slash.GetCString());
72   EXPECT_STREQ("bar", fs_windows_trailing_slash.GetFilename().GetCString());
73 }
74 
75 TEST(FileSpecTest, AppendPathComponent) {
76   FileSpec fs_posix("/foo", FileSpec::Style::posix);
77   fs_posix.AppendPathComponent("bar");
78   EXPECT_STREQ("/foo/bar", fs_posix.GetCString());
79   EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString());
80   EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString());
81 
82   FileSpec fs_posix_2("/foo", FileSpec::Style::posix);
83   fs_posix_2.AppendPathComponent("//bar/baz");
84   EXPECT_STREQ("/foo/bar/baz", fs_posix_2.GetCString());
85   EXPECT_STREQ("/foo/bar", fs_posix_2.GetDirectory().GetCString());
86   EXPECT_STREQ("baz", fs_posix_2.GetFilename().GetCString());
87 
88   FileSpec fs_windows("F:\\bar", FileSpec::Style::windows);
89   fs_windows.AppendPathComponent("baz");
90   EXPECT_STREQ("F:\\bar\\baz", fs_windows.GetCString());
91   // EXPECT_STREQ("F:\\bar", fs_windows.GetDirectory().GetCString()); // It
92   // returns "F:/bar"
93   EXPECT_STREQ("baz", fs_windows.GetFilename().GetCString());
94 
95   FileSpec fs_posix_root("/", FileSpec::Style::posix);
96   fs_posix_root.AppendPathComponent("bar");
97   EXPECT_STREQ("/bar", fs_posix_root.GetCString());
98   EXPECT_STREQ("/", fs_posix_root.GetDirectory().GetCString());
99   EXPECT_STREQ("bar", fs_posix_root.GetFilename().GetCString());
100 
101   FileSpec fs_windows_root("F:\\", FileSpec::Style::windows);
102   fs_windows_root.AppendPathComponent("bar");
103   EXPECT_STREQ("F:\\bar", fs_windows_root.GetCString());
104   // EXPECT_STREQ("F:\\", fs_windows_root.GetDirectory().GetCString()); // It
105   // returns "F:/"
106   EXPECT_STREQ("bar", fs_windows_root.GetFilename().GetCString());
107 }
108 
109 TEST(FileSpecTest, CopyByAppendingPathComponent) {
110   FileSpec fs = FileSpec("/foo", FileSpec::Style::posix)
111                     .CopyByAppendingPathComponent("bar");
112   EXPECT_STREQ("/foo/bar", fs.GetCString());
113   EXPECT_STREQ("/foo", fs.GetDirectory().GetCString());
114   EXPECT_STREQ("bar", fs.GetFilename().GetCString());
115 }
116 
117 TEST(FileSpecTest, PrependPathComponent) {
118   FileSpec fs_posix("foo", FileSpec::Style::posix);
119   fs_posix.PrependPathComponent("/bar");
120   EXPECT_STREQ("/bar/foo", fs_posix.GetCString());
121 
122   FileSpec fs_posix_2("foo/bar", FileSpec::Style::posix);
123   fs_posix_2.PrependPathComponent("/baz");
124   EXPECT_STREQ("/baz/foo/bar", fs_posix_2.GetCString());
125 
126   FileSpec fs_windows("baz", FileSpec::Style::windows);
127   fs_windows.PrependPathComponent("F:\\bar");
128   EXPECT_STREQ("F:\\bar\\baz", fs_windows.GetCString());
129 
130   FileSpec fs_posix_root("bar", FileSpec::Style::posix);
131   fs_posix_root.PrependPathComponent("/");
132   EXPECT_STREQ("/bar", fs_posix_root.GetCString());
133 
134   FileSpec fs_windows_root("bar", FileSpec::Style::windows);
135   fs_windows_root.PrependPathComponent("F:\\");
136   EXPECT_STREQ("F:\\bar", fs_windows_root.GetCString());
137 }
138 
139 TEST(FileSpecTest, EqualSeparator) {
140   FileSpec backward("C:\\foo\\bar", FileSpec::Style::windows);
141   FileSpec forward("C:/foo/bar", FileSpec::Style::windows);
142   EXPECT_EQ(forward, backward);
143 }
144 
145 TEST(FileSpecTest, EqualDotsWindows) {
146   std::pair<const char *, const char *> tests[] = {
147       {R"(C:\foo\bar\baz)", R"(C:\foo\foo\..\bar\baz)"},
148       {R"(C:\bar\baz)", R"(C:\foo\..\bar\baz)"},
149       {R"(C:\bar\baz)", R"(C:/foo/../bar/baz)"},
150       {R"(C:/bar/baz)", R"(C:\foo\..\bar\baz)"},
151       {R"(C:\bar)", R"(C:\foo\..\bar)"},
152       {R"(C:\foo\bar)", R"(C:\foo\.\bar)"},
153       {R"(C:\foo\bar)", R"(C:\foo\bar\.)"},
154   };
155 
156   for (const auto &test : tests) {
157     FileSpec one(test.first, FileSpec::Style::windows);
158     FileSpec two(test.second, FileSpec::Style::windows);
159     EXPECT_EQ(one, two);
160   }
161 }
162 
163 TEST(FileSpecTest, EqualDotsPosix) {
164   std::pair<const char *, const char *> tests[] = {
165       {R"(/foo/bar/baz)", R"(/foo/foo/../bar/baz)"},
166       {R"(/bar/baz)", R"(/foo/../bar/baz)"},
167       {R"(/bar)", R"(/foo/../bar)"},
168       {R"(/foo/bar)", R"(/foo/./bar)"},
169       {R"(/foo/bar)", R"(/foo/bar/.)"},
170   };
171 
172   for (const auto &test : tests) {
173     FileSpec one(test.first, FileSpec::Style::posix);
174     FileSpec two(test.second, FileSpec::Style::posix);
175     EXPECT_EQ(one, two);
176   }
177 }
178 
179 TEST(FileSpecTest, EqualDotsPosixRoot) {
180   std::pair<const char *, const char *> tests[] = {
181       {R"(/)", R"(/..)"},
182       {R"(/)", R"(/.)"},
183       {R"(/)", R"(/foo/..)"},
184   };
185 
186   for (const auto &test : tests) {
187     FileSpec one(test.first, FileSpec::Style::posix);
188     FileSpec two(test.second, FileSpec::Style::posix);
189     EXPECT_EQ(one, two);
190   }
191 }
192 
193 TEST(FileSpecTest, GetNormalizedPath) {
194   std::pair<const char *, const char *> posix_tests[] = {
195       {"/foo/.././bar", "/bar"},
196       {"/foo/./../bar", "/bar"},
197       {"/foo/../bar", "/bar"},
198       {"/foo/./bar", "/foo/bar"},
199       {"/foo/..", "/"},
200       {"/foo/.", "/foo"},
201       {"/foo//bar", "/foo/bar"},
202       {"/foo//bar/baz", "/foo/bar/baz"},
203       {"/foo//bar/./baz", "/foo/bar/baz"},
204       {"/./foo", "/foo"},
205       {"/", "/"},
206       {"//", "/"},
207       {"//net", "//net"},
208       {"/..", "/"},
209       {"/.", "/"},
210       {"..", ".."},
211       {".", "."},
212       {"../..", "../.."},
213       {"foo/..", "."},
214       {"foo/../bar", "bar"},
215       {"../foo/..", ".."},
216       {"./foo", "foo"},
217       {"././foo", "foo"},
218       {"../foo", "../foo"},
219       {"../../foo", "../../foo"},
220   };
221   for (auto test : posix_tests) {
222     SCOPED_TRACE(llvm::Twine("test.first = ") + test.first);
223     EXPECT_EQ(test.second,
224               FileSpec(test.first, FileSpec::Style::posix).GetPath());
225   }
226 
227   std::pair<const char *, const char *> windows_tests[] = {
228       {R"(c:\bar\..\bar)", R"(c:\bar)"},
229       {R"(c:\bar\.\bar)", R"(c:\bar\bar)"},
230       {R"(c:\bar\..)", R"(c:\)"},
231       {R"(c:\bar\.)", R"(c:\bar)"},
232       {R"(c:\.\bar)", R"(c:\bar)"},
233       {R"(\)", R"(\)"},
234       {R"(\\)", R"(\)"},
235       {R"(\\net)", R"(\\net)"},
236       {R"(c:\..)", R"(c:\)"},
237       {R"(c:\.)", R"(c:\)"},
238       // TODO: fix llvm::sys::path::remove_dots() to return "\" below.
239       {R"(\..)", R"(\..)"},
240       //      {R"(c:..)", R"(c:..)"},
241       {R"(..)", R"(..)"},
242       {R"(.)", R"(.)"},
243       // TODO: fix llvm::sys::path::remove_dots() to return "c:\" below.
244       {R"(c:..\..)", R"(c:\..\..)"},
245       {R"(..\..)", R"(..\..)"},
246       {R"(foo\..)", R"(.)"},
247       {R"(foo\..\bar)", R"(bar)"},
248       {R"(..\foo\..)", R"(..)"},
249       {R"(.\foo)", R"(foo)"},
250       {R"(.\.\foo)", R"(foo)"},
251       {R"(..\foo)", R"(..\foo)"},
252       {R"(..\..\foo)", R"(..\..\foo)"},
253   };
254   for (auto test : windows_tests) {
255     EXPECT_EQ(test.second,
256               FileSpec(test.first, FileSpec::Style::windows).GetPath())
257         << "Original path: " << test.first;
258   }
259 }
260 
261 TEST(FileSpecTest, FormatFileSpec) {
262   auto win = FileSpec::Style::windows;
263 
264   FileSpec F;
265   EXPECT_EQ("(empty)", llvm::formatv("{0}", F).str());
266   EXPECT_EQ("(empty)", llvm::formatv("{0:D}", F).str());
267   EXPECT_EQ("(empty)", llvm::formatv("{0:F}", F).str());
268 
269   F = FileSpec("C:\\foo\\bar.txt", win);
270   EXPECT_EQ("C:\\foo\\bar.txt", llvm::formatv("{0}", F).str());
271   EXPECT_EQ("C:\\foo\\", llvm::formatv("{0:D}", F).str());
272   EXPECT_EQ("bar.txt", llvm::formatv("{0:F}", F).str());
273 
274   F = FileSpec("foo\\bar.txt", win);
275   EXPECT_EQ("foo\\bar.txt", llvm::formatv("{0}", F).str());
276   EXPECT_EQ("foo\\", llvm::formatv("{0:D}", F).str());
277   EXPECT_EQ("bar.txt", llvm::formatv("{0:F}", F).str());
278 
279   F = FileSpec("foo", win);
280   EXPECT_EQ("foo", llvm::formatv("{0}", F).str());
281   EXPECT_EQ("foo", llvm::formatv("{0:F}", F).str());
282   EXPECT_EQ("(empty)", llvm::formatv("{0:D}", F).str());
283 }
284 
285 TEST(FileSpecTest, IsRelative) {
286   llvm::StringRef not_relative[] = {
287     "/",
288     "/a",
289     "/a/",
290     "/a/b",
291     "/a/b/",
292     "//",
293     "//a/",
294     "//a/b",
295     "//a/b/",
296     "~",
297     "~/",
298     "~/a",
299     "~/a/",
300     "~/a/b"
301     "~/a/b/",
302     "/foo/.",
303     "/foo/..",
304     "/foo/../",
305     "/foo/../.",
306   };
307   for (const auto &path: not_relative) {
308     FileSpec spec(path, FileSpec::Style::posix);
309     EXPECT_FALSE(spec.IsRelative());
310   }
311   llvm::StringRef is_relative[] = {
312     ".",
313     "./",
314     ".///",
315     "a",
316     "./a",
317     "./a/",
318     "./a/",
319     "./a/b",
320     "./a/b/",
321     "../foo",
322     "foo/bar.c",
323     "./foo/bar.c"
324   };
325   for (const auto &path: is_relative) {
326     FileSpec spec(path, FileSpec::Style::posix);
327     EXPECT_TRUE(spec.IsRelative());
328   }
329 }
330 
331 TEST(FileSpecTest, RemoveLastPathComponent) {
332   FileSpec fs_posix("/foo/bar/baz", FileSpec::Style::posix);
333   EXPECT_STREQ("/foo/bar/baz", fs_posix.GetCString());
334   EXPECT_TRUE(fs_posix.RemoveLastPathComponent());
335   EXPECT_STREQ("/foo/bar", fs_posix.GetCString());
336   EXPECT_TRUE(fs_posix.RemoveLastPathComponent());
337   EXPECT_STREQ("/foo", fs_posix.GetCString());
338   EXPECT_TRUE(fs_posix.RemoveLastPathComponent());
339   EXPECT_STREQ("/", fs_posix.GetCString());
340   EXPECT_FALSE(fs_posix.RemoveLastPathComponent());
341   EXPECT_STREQ("/", fs_posix.GetCString());
342 
343   FileSpec fs_posix_relative("./foo/bar/baz", FileSpec::Style::posix);
344   EXPECT_STREQ("foo/bar/baz", fs_posix_relative.GetCString());
345   EXPECT_TRUE(fs_posix_relative.RemoveLastPathComponent());
346   EXPECT_STREQ("foo/bar", fs_posix_relative.GetCString());
347   EXPECT_TRUE(fs_posix_relative.RemoveLastPathComponent());
348   EXPECT_STREQ("foo", fs_posix_relative.GetCString());
349   EXPECT_FALSE(fs_posix_relative.RemoveLastPathComponent());
350   EXPECT_STREQ("foo", fs_posix_relative.GetCString());
351 
352   FileSpec fs_posix_relative2("./", FileSpec::Style::posix);
353   EXPECT_STREQ(".", fs_posix_relative2.GetCString());
354   EXPECT_FALSE(fs_posix_relative2.RemoveLastPathComponent());
355   EXPECT_STREQ(".", fs_posix_relative2.GetCString());
356   EXPECT_FALSE(fs_posix_relative.RemoveLastPathComponent());
357   EXPECT_STREQ(".", fs_posix_relative2.GetCString());
358 
359   FileSpec fs_windows("C:\\foo\\bar\\baz", FileSpec::Style::windows);
360   EXPECT_STREQ("C:\\foo\\bar\\baz", fs_windows.GetCString());
361   EXPECT_TRUE(fs_windows.RemoveLastPathComponent());
362   EXPECT_STREQ("C:\\foo\\bar", fs_windows.GetCString());
363   EXPECT_TRUE(fs_windows.RemoveLastPathComponent());
364   EXPECT_STREQ("C:\\foo", fs_windows.GetCString());
365   EXPECT_TRUE(fs_windows.RemoveLastPathComponent());
366   EXPECT_STREQ("C:\\", fs_windows.GetCString());
367   EXPECT_TRUE(fs_windows.RemoveLastPathComponent());
368   EXPECT_STREQ("C:", fs_windows.GetCString());
369   EXPECT_FALSE(fs_windows.RemoveLastPathComponent());
370   EXPECT_STREQ("C:", fs_windows.GetCString());
371 }
372