clang-tools 10.0.0

DraftStoreTests.cpp
Go to the documentation of this file.
1 //===-- DraftStoreTests.cpp -------------------------------------*- C++ -*-===//
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 "Annotations.h"
10 #include "DraftStore.h"
11 #include "SourceCode.h"
12 #include "gmock/gmock.h"
13 #include "gtest/gtest.h"
14
15 namespace clang {
16 namespace clangd {
17 namespace {
18
19 struct IncrementalTestStep {
20 llvm::StringRef Src;
21 llvm::StringRef Contents;
22 };
23
24 int rangeLength(llvm::StringRef Code, const Range &Rng) {
25 llvm::Expected<size_t> Start = positionToOffset(Code, Rng.start);
26 llvm::Expected<size_t> End = positionToOffset(Code, Rng.end);
27 assert(Start);
28 assert(End);
29 return *End - *Start;
30 }
31
32 /// Send the changes one by one to updateDraft, verify the intermediate results.
33 void stepByStep(llvm::ArrayRef<IncrementalTestStep> Steps) {
34 DraftStore DS;
35 Annotations InitialSrc(Steps.front().Src);
36 constexpr llvm::StringLiteral Path("/hello.cpp");
37
38 // Set the initial content.
39 DS.addDraft(Path, InitialSrc.code());
40
41 for (size_t i = 1; i < Steps.size(); i++) {
42 Annotations SrcBefore(Steps[i - 1].Src);
43 Annotations SrcAfter(Steps[i].Src);
44 llvm::StringRef Contents = Steps[i - 1].Contents;
45 TextDocumentContentChangeEvent Event{
46 SrcBefore.range(),
47 rangeLength(SrcBefore.code(), SrcBefore.range()),
48 Contents.str(),
49 };
50
51 llvm::Expected<std::string> Result = DS.updateDraft(Path, {Event});
52 ASSERT_TRUE(!!Result);
53 EXPECT_EQ(*Result, SrcAfter.code());
54 EXPECT_EQ(*DS.getDraft(Path), SrcAfter.code());
55 }
56 }
57
58 /// Send all the changes at once to updateDraft, check only the final result.
59 void allAtOnce(llvm::ArrayRef<IncrementalTestStep> Steps) {
60 DraftStore DS;
61 Annotations InitialSrc(Steps.front().Src);
62 Annotations FinalSrc(Steps.back().Src);
63 constexpr llvm::StringLiteral Path("/hello.cpp");
64 std::vector<TextDocumentContentChangeEvent> Changes;
65
66 for (size_t i = 0; i < Steps.size() - 1; i++) {
67 Annotations Src(Steps[i].Src);
68 llvm::StringRef Contents = Steps[i].Contents;
69
70 Changes.push_back({
71 Src.range(),
72 rangeLength(Src.code(), Src.range()),
73 Contents.str(),
74 });
75 }
76
77 // Set the initial content.
78 DS.addDraft(Path, InitialSrc.code());
79
80 llvm::Expected<std::string> Result = DS.updateDraft(Path, Changes);
81
82 ASSERT_TRUE(!!Result) << llvm::toString(Result.takeError());
83 EXPECT_EQ(*Result, FinalSrc.code());
84 EXPECT_EQ(*DS.getDraft(Path), FinalSrc.code());
85 }
86
87 TEST(DraftStoreIncrementalUpdateTest, Simple) {
88 // clang-format off
89 IncrementalTestStep Steps[] =
90 {
91 // Replace a range
92 {
93 R"cpp(static int
94 hello[[World]]()
95 {})cpp",
96 "Universe"
97 },
98 // Delete a range
99 {
100 R"cpp(static int
101 hello[[Universe]]()
102 {})cpp",
103 ""
104 },
105 // Add a range
106 {
107 R"cpp(static int
108 hello[[]]()
109 {})cpp",
110 "Monde"
111 },
112 {
113 R"cpp(static int
114 helloMonde()
115 {})cpp",
116 ""
117 }
118 };
119 // clang-format on
120
121 stepByStep(Steps);
122 allAtOnce(Steps);
123 }
124
125 TEST(DraftStoreIncrementalUpdateTest, MultiLine) {
126 // clang-format off
127 IncrementalTestStep Steps[] =
128 {
129 // Replace a range
130 {
131 R"cpp(static [[int
132 helloWorld]]()
133 {})cpp", R"cpp(char
134 welcome)cpp"
135 },
136 // Delete a range
137 {
138 R"cpp(static char[[
139 welcome]]()
140 {})cpp",
141 ""
142 },
143 // Add a range
144 {
145 R"cpp(static char[[]]()
146 {})cpp",
147 R"cpp(
148 cookies)cpp"
149 },
150 // Replace the whole file
151 {
152 R"cpp([[static char
153 cookies()
154 {}]])cpp",
155 R"cpp(#include <stdio.h>
156 )cpp"
157 },
158 // Delete the whole file
159 {
160 R"cpp([[#include <stdio.h>
161 ]])cpp",
162 "",
163 },
164 // Add something to an empty file
165 {
166 "[[]]",
167 R"cpp(int main() {
168 )cpp",
169 },
170 {
171 R"cpp(int main() {
172 )cpp",
173 ""
174 }
175 };
176 // clang-format on
177
178 stepByStep(Steps);
179 allAtOnce(Steps);
180 }
181
182 TEST(DraftStoreIncrementalUpdateTest, WrongRangeLength) {
183 DraftStore DS;
184 Path File = "foo.cpp";
185
186 DS.addDraft(File, "int main() {}\n");
187
188 TextDocumentContentChangeEvent Change;
189 Change.range.emplace();
190 Change.range->start.line = 0;
191 Change.range->start.character = 0;
192 Change.range->end.line = 0;
193 Change.range->end.character = 2;
194 Change.rangeLength = 10;
195
196 Expected<std::string> Result = DS.updateDraft(File, {Change});
197
198 EXPECT_TRUE(!Result);
199 EXPECT_EQ(
200 toString(Result.takeError()),
201 "Change's rangeLength (10) doesn't match the computed range length (2).");
202 }
203
204 TEST(DraftStoreIncrementalUpdateTest, EndBeforeStart) {
205 DraftStore DS;
206 Path File = "foo.cpp";
207
208 DS.addDraft(File, "int main() {}\n");
209
210 TextDocumentContentChangeEvent Change;
211 Change.range.emplace();
212 Change.range->start.line = 0;
213 Change.range->start.character = 5;
214 Change.range->end.line = 0;
215 Change.range->end.character = 3;
216
217 Expected<std::string> Result = DS.updateDraft(File, {Change});
218
219 EXPECT_TRUE(!Result);
220 EXPECT_EQ(toString(Result.takeError()),
221 "Range's end position (0:3) is before start position (0:5)");
222 }
223
224 TEST(DraftStoreIncrementalUpdateTest, StartCharOutOfRange) {
225 DraftStore DS;
226 Path File = "foo.cpp";
227
228 DS.addDraft(File, "int main() {}\n");
229
230 TextDocumentContentChangeEvent Change;
231 Change.range.emplace();
232 Change.range->start.line = 0;
233 Change.range->start.character = 100;
234 Change.range->end.line = 0;
235 Change.range->end.character = 100;
236 Change.text = "foo";
237
238 Expected<std::string> Result = DS.updateDraft(File, {Change});
239
240 EXPECT_TRUE(!Result);
241 EXPECT_EQ(toString(Result.takeError()),
242 "utf-16 offset 100 is invalid for line 0");
243 }
244
245 TEST(DraftStoreIncrementalUpdateTest, EndCharOutOfRange) {
246 DraftStore DS;
247 Path File = "foo.cpp";
248
249 DS.addDraft(File, "int main() {}\n");
250
251 TextDocumentContentChangeEvent Change;
252 Change.range.emplace();
253 Change.range->start.line = 0;
254 Change.range->start.character = 0;
255 Change.range->end.line = 0;
256 Change.range->end.character = 100;
257 Change.text = "foo";
258
259 Expected<std::string> Result = DS.updateDraft(File, {Change});
260
261 EXPECT_TRUE(!Result);
262 EXPECT_EQ(toString(Result.takeError()),
263 "utf-16 offset 100 is invalid for line 0");
264 }
265
266 TEST(DraftStoreIncrementalUpdateTest, StartLineOutOfRange) {
267 DraftStore DS;
268 Path File = "foo.cpp";
269
270 DS.addDraft(File, "int main() {}\n");
271
272 TextDocumentContentChangeEvent Change;
273 Change.range.emplace();
274 Change.range->start.line = 100;
275 Change.range->start.character = 0;
276 Change.range->end.line = 100;
277 Change.range->end.character = 0;
278 Change.text = "foo";
279
280 Expected<std::string> Result = DS.updateDraft(File, {Change});
281
282 EXPECT_TRUE(!Result);
283 EXPECT_EQ(toString(Result.takeError()), "Line value is out of range (100)");
284 }
285
286 TEST(DraftStoreIncrementalUpdateTest, EndLineOutOfRange) {
287 DraftStore DS;
288 Path File = "foo.cpp";
289
290 DS.addDraft(File, "int main() {}\n");
291
292 TextDocumentContentChangeEvent Change;
293 Change.range.emplace();
294 Change.range->start.line = 0;
295 Change.range->start.character = 0;
296 Change.range->end.line = 100;
297 Change.range->end.character = 0;
298 Change.text = "foo";
299
300 Expected<std::string> Result = DS.updateDraft(File, {Change});
301
302 EXPECT_TRUE(!Result);
303 EXPECT_EQ(toString(Result.takeError()), "Line value is out of range (100)");
304 }
305
306 /// Check that if a valid change is followed by an invalid change, the original
307 /// version of the document (prior to all changes) is kept.
308 TEST(DraftStoreIncrementalUpdateTest, InvalidRangeInASequence) {
309 DraftStore DS;
310 Path File = "foo.cpp";
311
312 StringRef OriginalContents = "int main() {}\n";
313 DS.addDraft(File, OriginalContents);
314
315 // The valid change
316 TextDocumentContentChangeEvent Change1;
317 Change1.range.emplace();
318 Change1.range->start.line = 0;
319 Change1.range->start.character = 0;
320 Change1.range->end.line = 0;
321 Change1.range->end.character = 0;
322 Change1.text = "Hello ";
323
324 // The invalid change
325 TextDocumentContentChangeEvent Change2;
326 Change2.range.emplace();
327 Change2.range->start.line = 0;
328 Change2.range->start.character = 5;
329 Change2.range->end.line = 0;
330 Change2.range->end.character = 100;
331 Change2.text = "something";
332
333 Expected<std::string> Result = DS.updateDraft(File, {Change1, Change2});
334
335 EXPECT_TRUE(!Result);
336 EXPECT_EQ(toString(Result.takeError()),
337 "utf-16 offset 100 is invalid for line 0");
338
339 Optional<std::string> Contents = DS.getDraft(File);
340 EXPECT_TRUE(Contents);
341 EXPECT_EQ(*Contents, OriginalContents);
342 }
343
344 } // namespace
345 } // namespace clangd
346 } // namespace clang
347
Code
std::string Code
Definition: FindTargetTests.cpp:67
Changes
tooling::Replacements Changes
Definition: Format.cpp:108
SourceCode.h
clang::tidy::cppcoreguidelines::toString
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Definition: SpecialMemberFunctionsCheck.cpp:58
Src
llvm::StringRef Src
Definition: DraftStoreTests.cpp:20
Contents
llvm::StringRef Contents
Definition: DraftStoreTests.cpp:21
Annotations.h
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:647
clang::clangd::positionToOffset
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
Definition: SourceCode.cpp:155
clang::clangd::toString
static const char * toString(OffsetEncoding OE)
Definition: Protocol.cpp:1031
clang::clangd::Path
std::string Path
A typedef to represent a file path.
Definition: Path.h:20
clang::clangd::CompletionItemKind::Event
DraftStore.h
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::clangd::CompletionItemKind::File