1 //===-- CompletionRequestTest.cpp -----------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "gtest/gtest.h"
10 
11 #include "lldb/Utility/CompletionRequest.h"
12 using namespace lldb_private;
13 
14 TEST(CompletionRequest, Constructor) {
15   std::string command = "a bad c";
16   const unsigned cursor_pos = 3;
17   const size_t arg_index = 1;
18   StringList matches;
19   CompletionResult result;
20 
21   CompletionRequest request(command, cursor_pos, result);
22   result.GetMatches(matches);
23 
24   EXPECT_STREQ(request.GetRawLine().str().c_str(), command.c_str());
25   EXPECT_EQ(request.GetRawCursorPos(), cursor_pos);
26   EXPECT_EQ(request.GetCursorIndex(), arg_index);
27 
28   EXPECT_EQ(request.GetParsedLine().GetArgumentCount(), 2u);
29   EXPECT_EQ(request.GetCursorArgumentPrefix().str(), "b");
30 }
31 
32 TEST(CompletionRequest, FakeLastArg) {
33   // We insert an empty fake argument into the argument list when the
34   // cursor is after a space.
35   std::string command = "a bad c ";
36   const unsigned cursor_pos = command.size();
37   CompletionResult result;
38 
39   CompletionRequest request(command, cursor_pos, result);
40 
41   EXPECT_STREQ(request.GetRawLine().str().c_str(), command.c_str());
42   EXPECT_EQ(request.GetRawCursorPos(), cursor_pos);
43   EXPECT_EQ(request.GetCursorIndex(), 3U);
44 
45   EXPECT_EQ(request.GetParsedLine().GetArgumentCount(), 4U);
46   EXPECT_EQ(request.GetCursorArgumentPrefix().str(), "");
47 }
48 
49 TEST(CompletionRequest, TryCompleteCurrentArgGood) {
50   std::string command = "a bad c";
51   StringList matches, descriptions;
52   CompletionResult result;
53 
54   CompletionRequest request(command, 3, result);
55   request.TryCompleteCurrentArg("boo", "car");
56   result.GetMatches(matches);
57   result.GetDescriptions(descriptions);
58 
59   EXPECT_EQ(1U, result.GetResults().size());
60   EXPECT_STREQ("boo", matches.GetStringAtIndex(0U));
61   EXPECT_EQ(1U, descriptions.GetSize());
62   EXPECT_STREQ("car", descriptions.GetStringAtIndex(0U));
63 }
64 
65 TEST(CompletionRequest, TryCompleteCurrentArgBad) {
66   std::string command = "a bad c";
67   CompletionResult result;
68 
69   CompletionRequest request(command, 3, result);
70   request.TryCompleteCurrentArg("car", "card");
71 
72   EXPECT_EQ(0U, result.GetResults().size());
73 }
74 
75 TEST(CompletionRequest, TryCompleteCurrentArgMode) {
76   std::string command = "a bad c";
77   CompletionResult result;
78 
79   CompletionRequest request(command, 3, result);
80   request.TryCompleteCurrentArg<CompletionMode::Partial>("bar", "bard");
81 
82   EXPECT_EQ(1U, result.GetResults().size());
83   EXPECT_EQ(CompletionMode::Partial, result.GetResults()[0].GetMode());
84 }
85 
86 TEST(CompletionRequest, ShiftArguments) {
87   std::string command = "a bad c";
88   const unsigned cursor_pos = 3;
89   const size_t arg_index = 1;
90   StringList matches;
91   CompletionResult result;
92 
93   CompletionRequest request(command, cursor_pos, result);
94   result.GetMatches(matches);
95 
96   EXPECT_STREQ(request.GetRawLine().str().c_str(), command.c_str());
97   EXPECT_EQ(request.GetRawCursorPos(), cursor_pos);
98   EXPECT_EQ(request.GetCursorIndex(), arg_index);
99 
100   EXPECT_EQ(request.GetParsedLine().GetArgumentCount(), 2u);
101   EXPECT_STREQ(request.GetParsedLine().GetArgumentAtIndex(1), "b");
102 
103   // Shift away the 'a' argument.
104   request.ShiftArguments();
105 
106   // The raw line/cursor stays identical.
107   EXPECT_STREQ(request.GetRawLine().str().c_str(), command.c_str());
108   EXPECT_EQ(request.GetRawCursorPos(), cursor_pos);
109 
110   // Partially parsed line and cursor should be updated.
111   EXPECT_EQ(request.GetCursorIndex(), arg_index - 1U);
112   EXPECT_EQ(request.GetParsedLine().GetArgumentCount(), 1u);
113   EXPECT_EQ(request.GetCursorArgumentPrefix().str(), "b");
114 }
115 
116 TEST(CompletionRequest, DuplicateFiltering) {
117   std::string command = "a bad c";
118   const unsigned cursor_pos = 3;
119   StringList matches;
120 
121   CompletionResult result;
122   CompletionRequest request(command, cursor_pos, result);
123   result.GetMatches(matches);
124 
125   EXPECT_EQ(0U, result.GetNumberOfResults());
126 
127   // Add foo twice
128   request.AddCompletion("foo");
129   result.GetMatches(matches);
130 
131   EXPECT_EQ(1U, result.GetNumberOfResults());
132   EXPECT_EQ(1U, matches.GetSize());
133   EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
134 
135   request.AddCompletion("foo");
136   result.GetMatches(matches);
137 
138   EXPECT_EQ(1U, result.GetNumberOfResults());
139   EXPECT_EQ(1U, matches.GetSize());
140   EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
141 
142   // Add bar twice
143   request.AddCompletion("bar");
144   result.GetMatches(matches);
145 
146   EXPECT_EQ(2U, result.GetNumberOfResults());
147   EXPECT_EQ(2U, matches.GetSize());
148   EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
149   EXPECT_STREQ("bar", matches.GetStringAtIndex(1));
150 
151   request.AddCompletion("bar");
152   result.GetMatches(matches);
153 
154   EXPECT_EQ(2U, result.GetNumberOfResults());
155   EXPECT_EQ(2U, matches.GetSize());
156   EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
157   EXPECT_STREQ("bar", matches.GetStringAtIndex(1));
158 
159   // Add foo again.
160   request.AddCompletion("foo");
161   result.GetMatches(matches);
162 
163   EXPECT_EQ(2U, result.GetNumberOfResults());
164   EXPECT_EQ(2U, matches.GetSize());
165   EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
166   EXPECT_STREQ("bar", matches.GetStringAtIndex(1));
167 
168   // Add something with an existing prefix
169   request.AddCompletion("foobar");
170   result.GetMatches(matches);
171 
172   EXPECT_EQ(3U, result.GetNumberOfResults());
173   EXPECT_EQ(3U, matches.GetSize());
174   EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
175   EXPECT_STREQ("bar", matches.GetStringAtIndex(1));
176   EXPECT_STREQ("foobar", matches.GetStringAtIndex(2));
177 }
178 
179 TEST(CompletionRequest, DuplicateFilteringWithComments) {
180   std::string command = "a bad c";
181   const unsigned cursor_pos = 3;
182   StringList matches, descriptions;
183 
184   CompletionResult result;
185   CompletionRequest request(command, cursor_pos, result);
186   result.GetMatches(matches);
187   result.GetDescriptions(descriptions);
188 
189   EXPECT_EQ(0U, result.GetNumberOfResults());
190 
191   // Add foo twice with same comment
192   request.AddCompletion("foo", "comment");
193   result.GetMatches(matches);
194   result.GetDescriptions(descriptions);
195 
196   EXPECT_EQ(1U, result.GetNumberOfResults());
197   EXPECT_EQ(1U, matches.GetSize());
198   EXPECT_EQ(1U, descriptions.GetSize());
199   EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
200   EXPECT_STREQ("comment", descriptions.GetStringAtIndex(0));
201 
202   request.AddCompletion("foo", "comment");
203   result.GetMatches(matches);
204   result.GetDescriptions(descriptions);
205 
206   EXPECT_EQ(1U, result.GetNumberOfResults());
207   EXPECT_EQ(1U, matches.GetSize());
208   EXPECT_EQ(1U, descriptions.GetSize());
209   EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
210   EXPECT_STREQ("comment", descriptions.GetStringAtIndex(0));
211 
212   // Add bar twice with different comments
213   request.AddCompletion("bar", "comment");
214   result.GetMatches(matches);
215   result.GetDescriptions(descriptions);
216 
217   EXPECT_EQ(2U, result.GetNumberOfResults());
218   EXPECT_EQ(2U, matches.GetSize());
219   EXPECT_EQ(2U, descriptions.GetSize());
220   EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
221   EXPECT_STREQ("bar", matches.GetStringAtIndex(1));
222 
223   request.AddCompletion("bar", "another comment");
224   result.GetMatches(matches);
225   result.GetDescriptions(descriptions);
226 
227   EXPECT_EQ(3U, result.GetNumberOfResults());
228   EXPECT_EQ(3U, matches.GetSize());
229   EXPECT_EQ(3U, descriptions.GetSize());
230   EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
231   EXPECT_STREQ("comment", descriptions.GetStringAtIndex(0));
232   EXPECT_STREQ("bar", matches.GetStringAtIndex(1));
233   EXPECT_STREQ("comment", descriptions.GetStringAtIndex(1));
234   EXPECT_STREQ("bar", matches.GetStringAtIndex(2));
235   EXPECT_STREQ("another comment", descriptions.GetStringAtIndex(2));
236 
237   // Add foo again with no comment
238   request.AddCompletion("foo");
239   result.GetMatches(matches);
240   result.GetDescriptions(descriptions);
241 
242   EXPECT_EQ(4U, result.GetNumberOfResults());
243   EXPECT_EQ(4U, matches.GetSize());
244   EXPECT_EQ(4U, descriptions.GetSize());
245   EXPECT_STREQ("foo", matches.GetStringAtIndex(0));
246   EXPECT_STREQ("comment", descriptions.GetStringAtIndex(0));
247   EXPECT_STREQ("bar", matches.GetStringAtIndex(1));
248   EXPECT_STREQ("comment", descriptions.GetStringAtIndex(1));
249   EXPECT_STREQ("bar", matches.GetStringAtIndex(2));
250   EXPECT_STREQ("another comment", descriptions.GetStringAtIndex(2));
251   EXPECT_STREQ("foo", matches.GetStringAtIndex(3));
252   EXPECT_STREQ("", descriptions.GetStringAtIndex(3));
253 }
254 
255 TEST(CompletionRequest, TestCompletionOwnership) {
256   std::string command = "a bad c";
257   const unsigned cursor_pos = 3;
258   StringList matches;
259 
260   CompletionResult result;
261   CompletionRequest request(command, cursor_pos, result);
262 
263   std::string Temporary = "bar";
264   request.AddCompletion(Temporary);
265   // Manipulate our completion. The request should have taken a copy, so that
266   // shouldn't influence anything.
267   Temporary[0] = 'f';
268 
269   result.GetMatches(matches);
270   EXPECT_EQ(1U, result.GetNumberOfResults());
271   EXPECT_STREQ("bar", matches.GetStringAtIndex(0));
272 }
273