1 //===-- TestCompletion.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 #include "lldb/Interpreter/CommandCompletions.h"
12 #include "lldb/Utility/StringList.h"
13 #include "lldb/Utility/TildeExpressionResolver.h"
14 
15 #include "TestingSupport/MockTildeExpressionResolver.h"
16 #include "llvm/ADT/SmallString.h"
17 #include "llvm/Support/FileSystem.h"
18 #include "llvm/Support/Path.h"
19 #include "llvm/Support/raw_ostream.h"
20 
21 namespace fs = llvm::sys::fs;
22 namespace path = llvm::sys::path;
23 using namespace llvm;
24 using namespace lldb_private;
25 
26 #define ASSERT_NO_ERROR(x)                                                     \
27   if (std::error_code ASSERT_NO_ERROR_ec = x) {                                \
28     SmallString<128> MessageStorage;                                           \
29     raw_svector_ostream Message(MessageStorage);                               \
30     Message << #x ": did not return errc::success.\n"                          \
31             << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n"          \
32             << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n";      \
33     GTEST_FATAL_FAILURE_(MessageStorage.c_str());                              \
34   } else {                                                                     \
35   }
36 
37 namespace {
38 
39 class CompletionTest : public testing::Test {
40 protected:
41   /// Unique temporary directory in which all created filesystem entities must
42   /// be placed. It is removed at the end of the test suite.
43   static SmallString<128> BaseDir;
44 
45   static SmallString<128> OriginalWorkingDir;
46 
47   static SmallString<128> DirFoo;
48   static SmallString<128> DirFooA;
49   static SmallString<128> DirFooB;
50   static SmallString<128> DirFooC;
51   static SmallString<128> DirBar;
52   static SmallString<128> DirBaz;
53   static SmallString<128> DirTestFolder;
54 
55   static SmallString<128> FileAA;
56   static SmallString<128> FileAB;
57   static SmallString<128> FileAC;
58   static SmallString<128> FileFoo;
59   static SmallString<128> FileBar;
60   static SmallString<128> FileBaz;
61 
62   void SetUp() override { llvm::sys::fs::set_current_path(OriginalWorkingDir); }
63 
64   static void SetUpTestCase() {
65     llvm::sys::fs::current_path(OriginalWorkingDir);
66 
67     ASSERT_NO_ERROR(fs::createUniqueDirectory("FsCompletion", BaseDir));
68     const char *DirNames[] = {"foo", "fooa", "foob",       "fooc",
69                               "bar", "baz",  "test_folder"};
70     const char *FileNames[] = {"aa1234.tmp",  "ab1234.tmp",  "ac1234.tmp",
71                                "foo1234.tmp", "bar1234.tmp", "baz1234.tmp"};
72     SmallString<128> *Dirs[] = {&DirFoo, &DirFooA, &DirFooB,      &DirFooC,
73                                 &DirBar, &DirBaz,  &DirTestFolder};
74     for (auto Dir : llvm::zip(DirNames, Dirs)) {
75       auto &Path = *std::get<1>(Dir);
76       Path = BaseDir;
77       path::append(Path, std::get<0>(Dir));
78       ASSERT_NO_ERROR(fs::create_directory(Path));
79     }
80 
81     SmallString<128> *Files[] = {&FileAA,  &FileAB,  &FileAC,
82                                  &FileFoo, &FileBar, &FileBaz};
83     for (auto File : llvm::zip(FileNames, Files)) {
84       auto &Path = *std::get<1>(File);
85       Path = BaseDir;
86       path::append(Path, std::get<0>(File));
87       int FD;
88       ASSERT_NO_ERROR(fs::createUniqueFile(Path, FD, Path));
89       ::close(FD);
90     }
91   }
92 
93   static void TearDownTestCase() {
94     ASSERT_NO_ERROR(fs::remove_directories(BaseDir));
95   }
96 
97   static bool HasEquivalentFile(const Twine &Path, const StringList &Paths) {
98     for (size_t I = 0; I < Paths.GetSize(); ++I) {
99       if (fs::equivalent(Path, Paths[I]))
100         return true;
101     }
102     return false;
103   }
104 
105   static bool ContainsExactString(const Twine &Str, const StringList &Paths) {
106     SmallString<16> Storage;
107     StringRef Rendered = Str.toStringRef(Storage);
108     for (size_t I = 0; I < Paths.GetSize(); ++I) {
109       if (Paths[I] == Rendered)
110         return true;
111     }
112     return false;
113   }
114 
115   void DoDirCompletions(const Twine &Prefix,
116                         StandardTildeExpressionResolver &Resolver,
117                         StringList &Results) {
118     // When a partial name matches, it returns all matches.  If it matches both
119     // a full name AND some partial names, it returns all of them.
120     uint32_t Count =
121         CommandCompletions::DiskDirectories(Prefix + "foo", Results, Resolver);
122     ASSERT_EQ(4u, Count);
123     ASSERT_EQ(Count, Results.GetSize());
124     EXPECT_TRUE(HasEquivalentFile(DirFoo, Results));
125     EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));
126     EXPECT_TRUE(HasEquivalentFile(DirFooB, Results));
127     EXPECT_TRUE(HasEquivalentFile(DirFooC, Results));
128 
129     // If it matches only partial names, it still works as expected.
130     Count = CommandCompletions::DiskDirectories(Twine(Prefix) + "b", Results,
131                                                 Resolver);
132     ASSERT_EQ(2u, Count);
133     ASSERT_EQ(Count, Results.GetSize());
134     EXPECT_TRUE(HasEquivalentFile(DirBar, Results));
135     EXPECT_TRUE(HasEquivalentFile(DirBaz, Results));
136   }
137 };
138 
139 SmallString<128> CompletionTest::BaseDir;
140 SmallString<128> CompletionTest::OriginalWorkingDir;
141 
142 SmallString<128> CompletionTest::DirFoo;
143 SmallString<128> CompletionTest::DirFooA;
144 SmallString<128> CompletionTest::DirFooB;
145 SmallString<128> CompletionTest::DirFooC;
146 SmallString<128> CompletionTest::DirBar;
147 SmallString<128> CompletionTest::DirBaz;
148 SmallString<128> CompletionTest::DirTestFolder;
149 
150 SmallString<128> CompletionTest::FileAA;
151 SmallString<128> CompletionTest::FileAB;
152 SmallString<128> CompletionTest::FileAC;
153 SmallString<128> CompletionTest::FileFoo;
154 SmallString<128> CompletionTest::FileBar;
155 SmallString<128> CompletionTest::FileBaz;
156 }
157 
158 TEST_F(CompletionTest, DirCompletionAbsolute) {
159   // All calls to DiskDirectories() return only directories, even when
160   // there are files which also match.  The tests below all check this
161   // by asserting an exact result count, and verifying against known
162   // folders.
163 
164   std::string Prefixes[] = {(Twine(BaseDir) + "/").str(), ""};
165 
166   StandardTildeExpressionResolver Resolver;
167   StringList Results;
168 
169   // When a directory is specified that doesn't end in a slash, it searches
170   // for that directory, not items under it.
171   size_t Count =
172       CommandCompletions::DiskDirectories(BaseDir, Results, Resolver);
173   ASSERT_EQ(1u, Count);
174   ASSERT_EQ(Count, Results.GetSize());
175   EXPECT_TRUE(HasEquivalentFile(BaseDir, Results));
176 
177   // When the same directory ends with a slash, it finds all children.
178   Count = CommandCompletions::DiskDirectories(Prefixes[0], Results, Resolver);
179   ASSERT_EQ(7u, Count);
180   ASSERT_EQ(Count, Results.GetSize());
181   EXPECT_TRUE(HasEquivalentFile(DirFoo, Results));
182   EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));
183   EXPECT_TRUE(HasEquivalentFile(DirFooB, Results));
184   EXPECT_TRUE(HasEquivalentFile(DirFooC, Results));
185   EXPECT_TRUE(HasEquivalentFile(DirBar, Results));
186   EXPECT_TRUE(HasEquivalentFile(DirBaz, Results));
187   EXPECT_TRUE(HasEquivalentFile(DirTestFolder, Results));
188 
189   DoDirCompletions(Twine(BaseDir) + "/", Resolver, Results);
190   llvm::sys::fs::set_current_path(BaseDir);
191   DoDirCompletions("", Resolver, Results);
192 }
193 
194 TEST_F(CompletionTest, FileCompletionAbsolute) {
195   // All calls to DiskFiles() return both files and directories  The tests below
196   // all check this by asserting an exact result count, and verifying against
197   // known folders.
198 
199   StandardTildeExpressionResolver Resolver;
200   StringList Results;
201   // When an item is specified that doesn't end in a slash but exactly matches
202   // one item, it returns that item.
203   size_t Count = CommandCompletions::DiskFiles(Twine(BaseDir) + "/fooa",
204                                                Results, Resolver);
205   ASSERT_EQ(1u, Count);
206   ASSERT_EQ(Count, Results.GetSize());
207   EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));
208 
209   // The previous check verified a directory match.  But it should work for
210   // files too.
211   Count =
212       CommandCompletions::DiskFiles(Twine(BaseDir) + "/aa", Results, Resolver);
213   ASSERT_EQ(1u, Count);
214   ASSERT_EQ(Count, Results.GetSize());
215   EXPECT_TRUE(HasEquivalentFile(FileAA, Results));
216 
217   // When it ends with a slash, it should find all files and directories.
218   Count =
219       CommandCompletions::DiskFiles(Twine(BaseDir) + "/", Results, Resolver);
220   ASSERT_EQ(13u, Count);
221   ASSERT_EQ(Count, Results.GetSize());
222   EXPECT_TRUE(HasEquivalentFile(DirFoo, Results));
223   EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));
224   EXPECT_TRUE(HasEquivalentFile(DirFooB, Results));
225   EXPECT_TRUE(HasEquivalentFile(DirFooC, Results));
226   EXPECT_TRUE(HasEquivalentFile(DirBar, Results));
227   EXPECT_TRUE(HasEquivalentFile(DirBaz, Results));
228   EXPECT_TRUE(HasEquivalentFile(DirTestFolder, Results));
229 
230   EXPECT_TRUE(HasEquivalentFile(FileAA, Results));
231   EXPECT_TRUE(HasEquivalentFile(FileAB, Results));
232   EXPECT_TRUE(HasEquivalentFile(FileAC, Results));
233   EXPECT_TRUE(HasEquivalentFile(FileFoo, Results));
234   EXPECT_TRUE(HasEquivalentFile(FileBar, Results));
235   EXPECT_TRUE(HasEquivalentFile(FileBaz, Results));
236 
237   // When a partial name matches, it returns all file & directory matches.
238   Count =
239       CommandCompletions::DiskFiles(Twine(BaseDir) + "/foo", Results, Resolver);
240   ASSERT_EQ(5u, Count);
241   ASSERT_EQ(Count, Results.GetSize());
242   EXPECT_TRUE(HasEquivalentFile(DirFoo, Results));
243   EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));
244   EXPECT_TRUE(HasEquivalentFile(DirFooB, Results));
245   EXPECT_TRUE(HasEquivalentFile(DirFooC, Results));
246   EXPECT_TRUE(HasEquivalentFile(FileFoo, Results));
247 }
248 
249 TEST_F(CompletionTest, DirCompletionUsername) {
250   MockTildeExpressionResolver Resolver("James", BaseDir);
251   Resolver.AddKnownUser("Kirk", DirFooB);
252   Resolver.AddKnownUser("Lars", DirFooC);
253   Resolver.AddKnownUser("Jason", DirFoo);
254   Resolver.AddKnownUser("Larry", DirFooA);
255 
256   // Just resolving current user's home directory by itself should return the
257   // directory.
258   StringList Results;
259   size_t Count = CommandCompletions::DiskDirectories("~", Results, Resolver);
260   ASSERT_EQ(1u, Count);
261   ASSERT_EQ(Count, Results.GetSize());
262   EXPECT_TRUE(ContainsExactString(Twine("~") + path::get_separator(), Results));
263 
264   // With a slash appended, it should return all items in the directory.
265   Count = CommandCompletions::DiskDirectories("~/", Results, Resolver);
266   ASSERT_EQ(7u, Count);
267   ASSERT_EQ(Count, Results.GetSize());
268   EXPECT_TRUE(
269       ContainsExactString(Twine("~/foo") + path::get_separator(), Results));
270   EXPECT_TRUE(
271       ContainsExactString(Twine("~/fooa") + path::get_separator(), Results));
272   EXPECT_TRUE(
273       ContainsExactString(Twine("~/foob") + path::get_separator(), Results));
274   EXPECT_TRUE(
275       ContainsExactString(Twine("~/fooc") + path::get_separator(), Results));
276   EXPECT_TRUE(
277       ContainsExactString(Twine("~/bar") + path::get_separator(), Results));
278   EXPECT_TRUE(
279       ContainsExactString(Twine("~/baz") + path::get_separator(), Results));
280   EXPECT_TRUE(ContainsExactString(
281       Twine("~/test_folder") + path::get_separator(), Results));
282 
283   // With ~username syntax it should return one match if there is an exact
284   // match.
285   // It shouldn't translate to the actual directory, it should keep the form the
286   // user typed.
287   Count = CommandCompletions::DiskDirectories("~Lars", Results, Resolver);
288   ASSERT_EQ(1u, Count);
289   ASSERT_EQ(Count, Results.GetSize());
290   EXPECT_TRUE(
291       ContainsExactString(Twine("~Lars") + path::get_separator(), Results));
292 
293   // But with a username that is not found, no results are returned.
294   Count = CommandCompletions::DiskDirectories("~Dave", Results, Resolver);
295   ASSERT_EQ(0u, Count);
296   ASSERT_EQ(Count, Results.GetSize());
297 
298   // And if there are multiple matches, it should return all of them.
299   Count = CommandCompletions::DiskDirectories("~La", Results, Resolver);
300   ASSERT_EQ(2u, Count);
301   ASSERT_EQ(Count, Results.GetSize());
302   EXPECT_TRUE(
303       ContainsExactString(Twine("~Lars") + path::get_separator(), Results));
304   EXPECT_TRUE(
305       ContainsExactString(Twine("~Larry") + path::get_separator(), Results));
306 }
307