diff options
author | Jordan Rupprecht <rupprecht@google.com> | 2019-05-14 21:58:59 +0000 |
---|---|---|
committer | Jordan Rupprecht <rupprecht@google.com> | 2019-05-14 21:58:59 +0000 |
commit | 46054fed6aeeabea27b9ba4a3ef81ab5ff9b9645 (patch) | |
tree | d12279f80b5729d0324f066002c838baa736fbd2 /clangd/unittests/DraftStoreTests.cpp | |
parent | 5026a9a16d10a2edf09be54c7225f49b5789c69e (diff) | |
parent | 0eb1ac6d1df856f065717226ef34d00679a211fe (diff) |
Creating branches/google/stable and tags/google/stable/2019-05-14 from r360103upstream/stable
git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/branches/google/stable@360714 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'clangd/unittests/DraftStoreTests.cpp')
-rw-r--r-- | clangd/unittests/DraftStoreTests.cpp | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/clangd/unittests/DraftStoreTests.cpp b/clangd/unittests/DraftStoreTests.cpp new file mode 100644 index 00000000..1840892c --- /dev/null +++ b/clangd/unittests/DraftStoreTests.cpp @@ -0,0 +1,347 @@ +//===-- DraftStoreTests.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Annotations.h" +#include "DraftStore.h" +#include "SourceCode.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { + +struct IncrementalTestStep { + llvm::StringRef Src; + llvm::StringRef Contents; +}; + +int rangeLength(llvm::StringRef Code, const Range &Rng) { + llvm::Expected<size_t> Start = positionToOffset(Code, Rng.start); + llvm::Expected<size_t> End = positionToOffset(Code, Rng.end); + assert(Start); + assert(End); + return *End - *Start; +} + +/// Send the changes one by one to updateDraft, verify the intermediate results. +void stepByStep(llvm::ArrayRef<IncrementalTestStep> Steps) { + DraftStore DS; + Annotations InitialSrc(Steps.front().Src); + constexpr llvm::StringLiteral Path("/hello.cpp"); + + // Set the initial content. + DS.addDraft(Path, InitialSrc.code()); + + for (size_t i = 1; i < Steps.size(); i++) { + Annotations SrcBefore(Steps[i - 1].Src); + Annotations SrcAfter(Steps[i].Src); + llvm::StringRef Contents = Steps[i - 1].Contents; + TextDocumentContentChangeEvent Event{ + SrcBefore.range(), + rangeLength(SrcBefore.code(), SrcBefore.range()), + Contents.str(), + }; + + llvm::Expected<std::string> Result = DS.updateDraft(Path, {Event}); + ASSERT_TRUE(!!Result); + EXPECT_EQ(*Result, SrcAfter.code()); + EXPECT_EQ(*DS.getDraft(Path), SrcAfter.code()); + } +} + +/// Send all the changes at once to updateDraft, check only the final result. +void allAtOnce(llvm::ArrayRef<IncrementalTestStep> Steps) { + DraftStore DS; + Annotations InitialSrc(Steps.front().Src); + Annotations FinalSrc(Steps.back().Src); + constexpr llvm::StringLiteral Path("/hello.cpp"); + std::vector<TextDocumentContentChangeEvent> Changes; + + for (size_t i = 0; i < Steps.size() - 1; i++) { + Annotations Src(Steps[i].Src); + llvm::StringRef Contents = Steps[i].Contents; + + Changes.push_back({ + Src.range(), + rangeLength(Src.code(), Src.range()), + Contents.str(), + }); + } + + // Set the initial content. + DS.addDraft(Path, InitialSrc.code()); + + llvm::Expected<std::string> Result = DS.updateDraft(Path, Changes); + + ASSERT_TRUE(!!Result) << llvm::toString(Result.takeError()); + EXPECT_EQ(*Result, FinalSrc.code()); + EXPECT_EQ(*DS.getDraft(Path), FinalSrc.code()); +} + +TEST(DraftStoreIncrementalUpdateTest, Simple) { + // clang-format off + IncrementalTestStep Steps[] = + { + // Replace a range + { +R"cpp(static int +hello[[World]]() +{})cpp", + "Universe" + }, + // Delete a range + { +R"cpp(static int +hello[[Universe]]() +{})cpp", + "" + }, + // Add a range + { +R"cpp(static int +hello[[]]() +{})cpp", + "Monde" + }, + { +R"cpp(static int +helloMonde() +{})cpp", + "" + } + }; + // clang-format on + + stepByStep(Steps); + allAtOnce(Steps); +} + +TEST(DraftStoreIncrementalUpdateTest, MultiLine) { + // clang-format off + IncrementalTestStep Steps[] = + { + // Replace a range + { +R"cpp(static [[int +helloWorld]]() +{})cpp", +R"cpp(char +welcome)cpp" + }, + // Delete a range + { +R"cpp(static char[[ +welcome]]() +{})cpp", + "" + }, + // Add a range + { +R"cpp(static char[[]]() +{})cpp", + R"cpp( +cookies)cpp" + }, + // Replace the whole file + { +R"cpp([[static char +cookies() +{}]])cpp", + R"cpp(#include <stdio.h> +)cpp" + }, + // Delete the whole file + { + R"cpp([[#include <stdio.h> +]])cpp", + "", + }, + // Add something to an empty file + { + "[[]]", + R"cpp(int main() { +)cpp", + }, + { + R"cpp(int main() { +)cpp", + "" + } + }; + // clang-format on + + stepByStep(Steps); + allAtOnce(Steps); +} + +TEST(DraftStoreIncrementalUpdateTest, WrongRangeLength) { + DraftStore DS; + Path File = "foo.cpp"; + + DS.addDraft(File, "int main() {}\n"); + + TextDocumentContentChangeEvent Change; + Change.range.emplace(); + Change.range->start.line = 0; + Change.range->start.character = 0; + Change.range->end.line = 0; + Change.range->end.character = 2; + Change.rangeLength = 10; + + Expected<std::string> Result = DS.updateDraft(File, {Change}); + + EXPECT_TRUE(!Result); + EXPECT_EQ( + toString(Result.takeError()), + "Change's rangeLength (10) doesn't match the computed range length (2)."); +} + +TEST(DraftStoreIncrementalUpdateTest, EndBeforeStart) { + DraftStore DS; + Path File = "foo.cpp"; + + DS.addDraft(File, "int main() {}\n"); + + TextDocumentContentChangeEvent Change; + Change.range.emplace(); + Change.range->start.line = 0; + Change.range->start.character = 5; + Change.range->end.line = 0; + Change.range->end.character = 3; + + Expected<std::string> Result = DS.updateDraft(File, {Change}); + + EXPECT_TRUE(!Result); + EXPECT_EQ(toString(Result.takeError()), + "Range's end position (0:3) is before start position (0:5)"); +} + +TEST(DraftStoreIncrementalUpdateTest, StartCharOutOfRange) { + DraftStore DS; + Path File = "foo.cpp"; + + DS.addDraft(File, "int main() {}\n"); + + TextDocumentContentChangeEvent Change; + Change.range.emplace(); + Change.range->start.line = 0; + Change.range->start.character = 100; + Change.range->end.line = 0; + Change.range->end.character = 100; + Change.text = "foo"; + + Expected<std::string> Result = DS.updateDraft(File, {Change}); + + EXPECT_TRUE(!Result); + EXPECT_EQ(toString(Result.takeError()), + "utf-16 offset 100 is invalid for line 0"); +} + +TEST(DraftStoreIncrementalUpdateTest, EndCharOutOfRange) { + DraftStore DS; + Path File = "foo.cpp"; + + DS.addDraft(File, "int main() {}\n"); + + TextDocumentContentChangeEvent Change; + Change.range.emplace(); + Change.range->start.line = 0; + Change.range->start.character = 0; + Change.range->end.line = 0; + Change.range->end.character = 100; + Change.text = "foo"; + + Expected<std::string> Result = DS.updateDraft(File, {Change}); + + EXPECT_TRUE(!Result); + EXPECT_EQ(toString(Result.takeError()), + "utf-16 offset 100 is invalid for line 0"); +} + +TEST(DraftStoreIncrementalUpdateTest, StartLineOutOfRange) { + DraftStore DS; + Path File = "foo.cpp"; + + DS.addDraft(File, "int main() {}\n"); + + TextDocumentContentChangeEvent Change; + Change.range.emplace(); + Change.range->start.line = 100; + Change.range->start.character = 0; + Change.range->end.line = 100; + Change.range->end.character = 0; + Change.text = "foo"; + + Expected<std::string> Result = DS.updateDraft(File, {Change}); + + EXPECT_TRUE(!Result); + EXPECT_EQ(toString(Result.takeError()), "Line value is out of range (100)"); +} + +TEST(DraftStoreIncrementalUpdateTest, EndLineOutOfRange) { + DraftStore DS; + Path File = "foo.cpp"; + + DS.addDraft(File, "int main() {}\n"); + + TextDocumentContentChangeEvent Change; + Change.range.emplace(); + Change.range->start.line = 0; + Change.range->start.character = 0; + Change.range->end.line = 100; + Change.range->end.character = 0; + Change.text = "foo"; + + Expected<std::string> Result = DS.updateDraft(File, {Change}); + + EXPECT_TRUE(!Result); + EXPECT_EQ(toString(Result.takeError()), "Line value is out of range (100)"); +} + +/// Check that if a valid change is followed by an invalid change, the original +/// version of the document (prior to all changes) is kept. +TEST(DraftStoreIncrementalUpdateTest, InvalidRangeInASequence) { + DraftStore DS; + Path File = "foo.cpp"; + + StringRef OriginalContents = "int main() {}\n"; + DS.addDraft(File, OriginalContents); + + // The valid change + TextDocumentContentChangeEvent Change1; + Change1.range.emplace(); + Change1.range->start.line = 0; + Change1.range->start.character = 0; + Change1.range->end.line = 0; + Change1.range->end.character = 0; + Change1.text = "Hello "; + + // The invalid change + TextDocumentContentChangeEvent Change2; + Change2.range.emplace(); + Change2.range->start.line = 0; + Change2.range->start.character = 5; + Change2.range->end.line = 0; + Change2.range->end.character = 100; + Change2.text = "something"; + + Expected<std::string> Result = DS.updateDraft(File, {Change1, Change2}); + + EXPECT_TRUE(!Result); + EXPECT_EQ(toString(Result.takeError()), + "utf-16 offset 100 is invalid for line 0"); + + Optional<std::string> Contents = DS.getDraft(File); + EXPECT_TRUE(Contents); + EXPECT_EQ(*Contents, OriginalContents); +} + +} // namespace +} // namespace clangd +} // namespace clang |