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