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", false, 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", false, 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("/", false, 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", false, 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/", false, 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:", false, 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:\\", false, 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", false, 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", false, 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/", false, 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\\", false,
71                                      FileSpec::Style::windows);
72   EXPECT_STREQ("F:\\bar", fs_windows_trailing_slash.GetCString());
73   EXPECT_STREQ("bar", fs_windows_trailing_slash.GetFilename().GetCString());
74 }
75 
76 TEST(FileSpecTest, AppendPathComponent) {
77   FileSpec fs_posix("/foo", false, FileSpec::Style::posix);
78   fs_posix.AppendPathComponent("bar");
79   EXPECT_STREQ("/foo/bar", fs_posix.GetCString());
80   EXPECT_STREQ("/foo", fs_posix.GetDirectory().GetCString());
81   EXPECT_STREQ("bar", fs_posix.GetFilename().GetCString());
82 
83   FileSpec fs_posix_2("/foo", false, FileSpec::Style::posix);
84   fs_posix_2.AppendPathComponent("//bar/baz");
85   EXPECT_STREQ("/foo/bar/baz", fs_posix_2.GetCString());
86   EXPECT_STREQ("/foo/bar", fs_posix_2.GetDirectory().GetCString());
87   EXPECT_STREQ("baz", fs_posix_2.GetFilename().GetCString());
88 
89   FileSpec fs_windows("F:\\bar", false, FileSpec::Style::windows);
90   fs_windows.AppendPathComponent("baz");
91   EXPECT_STREQ("F:\\bar\\baz", fs_windows.GetCString());
92   // EXPECT_STREQ("F:\\bar", fs_windows.GetDirectory().GetCString()); // It
93   // returns "F:/bar"
94   EXPECT_STREQ("baz", fs_windows.GetFilename().GetCString());
95 
96   FileSpec fs_posix_root("/", false, FileSpec::Style::posix);
97   fs_posix_root.AppendPathComponent("bar");
98   EXPECT_STREQ("/bar", fs_posix_root.GetCString());
99   EXPECT_STREQ("/", fs_posix_root.GetDirectory().GetCString());
100   EXPECT_STREQ("bar", fs_posix_root.GetFilename().GetCString());
101 
102   FileSpec fs_windows_root("F:\\", false, FileSpec::Style::windows);
103   fs_windows_root.AppendPathComponent("bar");
104   EXPECT_STREQ("F:\\bar", fs_windows_root.GetCString());
105   // EXPECT_STREQ("F:\\", fs_windows_root.GetDirectory().GetCString()); // It
106   // returns "F:/"
107   EXPECT_STREQ("bar", fs_windows_root.GetFilename().GetCString());
108 }
109 
110 TEST(FileSpecTest, CopyByAppendingPathComponent) {
111   FileSpec fs = FileSpec("/foo", false, FileSpec::Style::posix)
112                     .CopyByAppendingPathComponent("bar");
113   EXPECT_STREQ("/foo/bar", fs.GetCString());
114   EXPECT_STREQ("/foo", fs.GetDirectory().GetCString());
115   EXPECT_STREQ("bar", fs.GetFilename().GetCString());
116 }
117 
118 TEST(FileSpecTest, PrependPathComponent) {
119   FileSpec fs_posix("foo", false, FileSpec::Style::posix);
120   fs_posix.PrependPathComponent("/bar");
121   EXPECT_STREQ("/bar/foo", fs_posix.GetCString());
122 
123   FileSpec fs_posix_2("foo/bar", false, FileSpec::Style::posix);
124   fs_posix_2.PrependPathComponent("/baz");
125   EXPECT_STREQ("/baz/foo/bar", fs_posix_2.GetCString());
126 
127   FileSpec fs_windows("baz", false, FileSpec::Style::windows);
128   fs_windows.PrependPathComponent("F:\\bar");
129   EXPECT_STREQ("F:\\bar\\baz", fs_windows.GetCString());
130 
131   FileSpec fs_posix_root("bar", false, FileSpec::Style::posix);
132   fs_posix_root.PrependPathComponent("/");
133   EXPECT_STREQ("/bar", fs_posix_root.GetCString());
134 
135   FileSpec fs_windows_root("bar", false, FileSpec::Style::windows);
136   fs_windows_root.PrependPathComponent("F:\\");
137   EXPECT_STREQ("F:\\bar", fs_windows_root.GetCString());
138 }
139 
140 TEST(FileSpecTest, EqualSeparator) {
141   FileSpec backward("C:\\foo\\bar", false, FileSpec::Style::windows);
142   FileSpec forward("C:/foo/bar", false, FileSpec::Style::windows);
143   EXPECT_EQ(forward, backward);
144 }
145 
146 TEST(FileSpecTest, EqualDotsWindows) {
147   std::pair<const char *, const char *> tests[] = {
148       {R"(C:\foo\bar\baz)", R"(C:\foo\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/baz)", R"(C:\foo\..\bar\baz)"},
152       {R"(C:\bar)", R"(C:\foo\..\bar)"},
153       {R"(C:\foo\bar)", R"(C:\foo\.\bar)"},
154       {R"(C:\foo\bar)", R"(C:\foo\bar\.)"},
155   };
156 
157   for (const auto &test : tests) {
158     FileSpec one(test.first, false, FileSpec::Style::windows);
159     FileSpec two(test.second, false, FileSpec::Style::windows);
160     EXPECT_EQ(one, two);
161   }
162 }
163 
164 TEST(FileSpecTest, EqualDotsPosix) {
165   std::pair<const char *, const char *> tests[] = {
166       {R"(/foo/bar/baz)", R"(/foo/foo/../bar/baz)"},
167       {R"(/bar/baz)", R"(/foo/../bar/baz)"},
168       {R"(/bar)", R"(/foo/../bar)"},
169       {R"(/foo/bar)", R"(/foo/./bar)"},
170       {R"(/foo/bar)", R"(/foo/bar/.)"},
171   };
172 
173   for (const auto &test : tests) {
174     FileSpec one(test.first, false, FileSpec::Style::posix);
175     FileSpec two(test.second, false, FileSpec::Style::posix);
176     EXPECT_EQ(one, two);
177   }
178 }
179 
180 TEST(FileSpecTest, EqualDotsPosixRoot) {
181   std::pair<const char *, const char *> tests[] = {
182       {R"(/)", R"(/..)"},
183       {R"(/)", R"(/.)"},
184       {R"(/)", R"(/foo/..)"},
185   };
186 
187   for (const auto &test : tests) {
188     FileSpec one(test.first, false, FileSpec::Style::posix);
189     FileSpec two(test.second, false, FileSpec::Style::posix);
190     EXPECT_EQ(one, two);
191   }
192 }
193 
194 TEST(FileSpecTest, GetNormalizedPath) {
195   std::pair<const char *, const char *> posix_tests[] = {
196       {"/foo/.././bar", "/bar"},
197       {"/foo/./../bar", "/bar"},
198       {"/foo/../bar", "/bar"},
199       {"/foo/./bar", "/foo/bar"},
200       {"/foo/..", "/"},
201       {"/foo/.", "/foo"},
202       {"/foo//bar", "/foo/bar"},
203       {"/foo//bar/baz", "/foo/bar/baz"},
204       {"/foo//bar/./baz", "/foo/bar/baz"},
205       {"/./foo", "/foo"},
206       {"/", "/"},
207       {"//", "/"},
208       {"//net", "//net"},
209       {"/..", "/"},
210       {"/.", "/"},
211       {"..", ".."},
212       {".", "."},
213       {"../..", "../.."},
214       {"foo/..", "."},
215       {"foo/../bar", "bar"},
216       {"../foo/..", ".."},
217       {"./foo", "foo"},
218       {"././foo", "foo"},
219       {"../foo", "../foo"},
220       {"../../foo", "../../foo"},
221   };
222   for (auto test : posix_tests) {
223     SCOPED_TRACE(llvm::Twine("test.first = ") + test.first);
224     EXPECT_EQ(test.second,
225               FileSpec(test.first, false, FileSpec::Style::posix).GetPath());
226   }
227 
228   std::pair<const char *, const char *> windows_tests[] = {
229       {R"(c:\bar\..\bar)", R"(c:\bar)"},
230       {R"(c:\bar\.\bar)", R"(c:\bar\bar)"},
231       {R"(c:\bar\..)", R"(c:\)"},
232       {R"(c:\bar\.)", R"(c:\bar)"},
233       {R"(c:\.\bar)", R"(c:\bar)"},
234       {R"(\)", R"(\)"},
235       {R"(\\)", R"(\)"},
236       {R"(\\net)", R"(\\net)"},
237       {R"(c:\..)", R"(c:\)"},
238       {R"(c:\.)", R"(c:\)"},
239       // TODO: fix llvm::sys::path::remove_dots() to return "\" below.
240       {R"(\..)", R"(\..)"},
241       //      {R"(c:..)", R"(c:..)"},
242       {R"(..)", R"(..)"},
243       {R"(.)", R"(.)"},
244       // TODO: fix llvm::sys::path::remove_dots() to return "c:\" below.
245       {R"(c:..\..)", R"(c:\..\..)"},
246       {R"(..\..)", R"(..\..)"},
247       {R"(foo\..)", R"(.)"},
248       {R"(foo\..\bar)", R"(bar)"},
249       {R"(..\foo\..)", R"(..)"},
250       {R"(.\foo)", R"(foo)"},
251       {R"(.\.\foo)", R"(foo)"},
252       {R"(..\foo)", R"(..\foo)"},
253       {R"(..\..\foo)", R"(..\..\foo)"},
254   };
255   for (auto test : windows_tests) {
256     EXPECT_EQ(test.second,
257               FileSpec(test.first, false, FileSpec::Style::windows).GetPath())
258         << "Original path: " << test.first;
259   }
260 }
261 
262 TEST(FileSpecTest, FormatFileSpec) {
263   auto win = FileSpec::Style::windows;
264 
265   FileSpec F;
266   EXPECT_EQ("(empty)", llvm::formatv("{0}", F).str());
267   EXPECT_EQ("(empty)", llvm::formatv("{0:D}", F).str());
268   EXPECT_EQ("(empty)", llvm::formatv("{0:F}", F).str());
269 
270   F = FileSpec("C:\\foo\\bar.txt", false, win);
271   EXPECT_EQ("C:\\foo\\bar.txt", llvm::formatv("{0}", F).str());
272   EXPECT_EQ("C:\\foo\\", llvm::formatv("{0:D}", F).str());
273   EXPECT_EQ("bar.txt", llvm::formatv("{0:F}", F).str());
274 
275   F = FileSpec("foo\\bar.txt", false, win);
276   EXPECT_EQ("foo\\bar.txt", llvm::formatv("{0}", F).str());
277   EXPECT_EQ("foo\\", llvm::formatv("{0:D}", F).str());
278   EXPECT_EQ("bar.txt", llvm::formatv("{0:F}", F).str());
279 
280   F = FileSpec("foo", false, win);
281   EXPECT_EQ("foo", llvm::formatv("{0}", F).str());
282   EXPECT_EQ("foo", llvm::formatv("{0:F}", F).str());
283   EXPECT_EQ("(empty)", llvm::formatv("{0:D}", F).str());
284 }
285 
286 TEST(FileSpecTest, IsRelative) {
287   llvm::StringRef not_relative[] = {
288     "/",
289     "/a",
290     "/a/",
291     "/a/b",
292     "/a/b/",
293     "//",
294     "//a/",
295     "//a/b",
296     "//a/b/",
297     "~",
298     "~/",
299     "~/a",
300     "~/a/",
301     "~/a/b"
302     "~/a/b/",
303     "/foo/.",
304     "/foo/..",
305     "/foo/../",
306     "/foo/../.",
307   };
308   for (const auto &path: not_relative) {
309     FileSpec spec(path, false, FileSpec::Style::posix);
310     EXPECT_FALSE(spec.IsRelative());
311   }
312   llvm::StringRef is_relative[] = {
313     ".",
314     "./",
315     ".///",
316     "a",
317     "./a",
318     "./a/",
319     "./a/",
320     "./a/b",
321     "./a/b/",
322     "../foo",
323     "foo/bar.c",
324     "./foo/bar.c"
325   };
326   for (const auto &path: is_relative) {
327     FileSpec spec(path, false, FileSpec::Style::posix);
328     EXPECT_TRUE(spec.IsRelative());
329   }
330 }
331 
332 TEST(FileSpecTest, RemoveLastPathComponent) {
333   FileSpec fs_posix("/foo/bar/baz", false, FileSpec::Style::posix);
334   EXPECT_STREQ("/foo/bar/baz", fs_posix.GetCString());
335   EXPECT_TRUE(fs_posix.RemoveLastPathComponent());
336   EXPECT_STREQ("/foo/bar", fs_posix.GetCString());
337   EXPECT_TRUE(fs_posix.RemoveLastPathComponent());
338   EXPECT_STREQ("/foo", fs_posix.GetCString());
339   EXPECT_TRUE(fs_posix.RemoveLastPathComponent());
340   EXPECT_STREQ("/", fs_posix.GetCString());
341   EXPECT_FALSE(fs_posix.RemoveLastPathComponent());
342   EXPECT_STREQ("/", fs_posix.GetCString());
343 
344   FileSpec fs_posix_relative("./foo/bar/baz", false, FileSpec::Style::posix);
345   EXPECT_STREQ("foo/bar/baz", fs_posix_relative.GetCString());
346   EXPECT_TRUE(fs_posix_relative.RemoveLastPathComponent());
347   EXPECT_STREQ("foo/bar", fs_posix_relative.GetCString());
348   EXPECT_TRUE(fs_posix_relative.RemoveLastPathComponent());
349   EXPECT_STREQ("foo", fs_posix_relative.GetCString());
350   EXPECT_FALSE(fs_posix_relative.RemoveLastPathComponent());
351   EXPECT_STREQ("foo", fs_posix_relative.GetCString());
352 
353   FileSpec fs_posix_relative2("./", false, FileSpec::Style::posix);
354   EXPECT_STREQ(".", fs_posix_relative2.GetCString());
355   EXPECT_FALSE(fs_posix_relative2.RemoveLastPathComponent());
356   EXPECT_STREQ(".", fs_posix_relative2.GetCString());
357   EXPECT_FALSE(fs_posix_relative.RemoveLastPathComponent());
358   EXPECT_STREQ(".", fs_posix_relative2.GetCString());
359 
360   FileSpec fs_windows("C:\\foo\\bar\\baz", false, FileSpec::Style::windows);
361   EXPECT_STREQ("C:\\foo\\bar\\baz", fs_windows.GetCString());
362   EXPECT_TRUE(fs_windows.RemoveLastPathComponent());
363   EXPECT_STREQ("C:\\foo\\bar", fs_windows.GetCString());
364   EXPECT_TRUE(fs_windows.RemoveLastPathComponent());
365   EXPECT_STREQ("C:\\foo", fs_windows.GetCString());
366   EXPECT_TRUE(fs_windows.RemoveLastPathComponent());
367   EXPECT_STREQ("C:\\", fs_windows.GetCString());
368   EXPECT_TRUE(fs_windows.RemoveLastPathComponent());
369   EXPECT_STREQ("C:", fs_windows.GetCString());
370   EXPECT_FALSE(fs_windows.RemoveLastPathComponent());
371   EXPECT_STREQ("C:", fs_windows.GetCString());
372 }
373