diff clang-tools-extra/clangd/unittests/SourceCodeTests.cpp @ 221:79ff65ed7e25

LLVM12 Original
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Tue, 15 Jun 2021 19:15:29 +0900
parents 0572611fdcc8
children c4bab56944e8
line wrap: on
line diff
--- a/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp	Tue Jun 15 19:13:43 2021 +0900
+++ b/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp	Tue Jun 15 19:15:29 2021 +0900
@@ -27,6 +27,7 @@
 namespace {
 
 using llvm::Failed;
+using llvm::FailedWithMessage;
 using llvm::HasValue;
 
 MATCHER_P2(Pos, Line, Col, "") {
@@ -71,6 +72,21 @@
   EXPECT_EQ(lspLength("😂"), 1UL);
 }
 
+TEST(SourceCodeTests, lspLengthBadUTF8) {
+  // Results are not well-defined if source file isn't valid UTF-8.
+  // However we shouldn't crash or return something totally wild.
+  const char *BadUTF8[] = {"\xa0", "\xff\xff\xff\xff\xff"};
+
+  for (OffsetEncoding Encoding :
+       {OffsetEncoding::UTF8, OffsetEncoding::UTF16, OffsetEncoding::UTF32}) {
+    WithContextValue UTF32(kCurrentOffsetEncoding, Encoding);
+    for (const char *Bad : BadUTF8) {
+      EXPECT_GE(lspLength(Bad), 0u);
+      EXPECT_LE(lspLength(Bad), strlen(Bad));
+    }
+  }
+}
+
 // The = → 🡆 below are ASCII (1 byte), BMP (3 bytes), and astral (4 bytes).
 const char File[] = R"(0:0 = 0
 1:0 → 8
@@ -774,6 +790,239 @@
   EXPECT_TRUE(isHeaderFile("header.h", LangOpts));
 }
 
+TEST(SourceCodeTests, isKeywords) {
+  LangOptions LangOpts;
+  LangOpts.CPlusPlus20 = true;
+  EXPECT_TRUE(isKeyword("int", LangOpts));
+  EXPECT_TRUE(isKeyword("return", LangOpts));
+  EXPECT_TRUE(isKeyword("co_await", LangOpts));
+
+  // these are identifiers (not keywords!) with special meaning in some
+  // contexts.
+  EXPECT_FALSE(isKeyword("final", LangOpts));
+  EXPECT_FALSE(isKeyword("override", LangOpts));
+}
+
+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) {
+  std::string Code = Annotations(Steps.front().Src).code().str();
+
+  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(),
+    };
+
+    EXPECT_THAT_ERROR(applyChange(Code, Event), llvm::Succeeded());
+    EXPECT_EQ(Code, SrcAfter.code());
+  }
+}
+
+TEST(ApplyEditsTest, 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);
+}
+
+TEST(ApplyEditsTest, 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);
+}
+
+TEST(ApplyEditsTest, WrongRangeLength) {
+  std::string Code = "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;
+
+  EXPECT_THAT_ERROR(applyChange(Code, Change),
+                    FailedWithMessage("Change's rangeLength (10) doesn't match "
+                                      "the computed range length (2)."));
+}
+
+TEST(ApplyEditsTest, EndBeforeStart) {
+  std::string Code = "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;
+
+  EXPECT_THAT_ERROR(
+      applyChange(Code, Change),
+      FailedWithMessage(
+          "Range's end position (0:3) is before start position (0:5)"));
+}
+
+TEST(ApplyEditsTest, StartCharOutOfRange) {
+  std::string Code = "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";
+
+  EXPECT_THAT_ERROR(
+      applyChange(Code, Change),
+      FailedWithMessage("utf-16 offset 100 is invalid for line 0"));
+}
+
+TEST(ApplyEditsTest, EndCharOutOfRange) {
+  std::string Code = "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";
+
+  EXPECT_THAT_ERROR(
+      applyChange(Code, Change),
+      FailedWithMessage("utf-16 offset 100 is invalid for line 0"));
+}
+
+TEST(ApplyEditsTest, StartLineOutOfRange) {
+  std::string Code = "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";
+
+  EXPECT_THAT_ERROR(applyChange(Code, Change),
+                    FailedWithMessage("Line value is out of range (100)"));
+}
+
+TEST(ApplyEditsTest, EndLineOutOfRange) {
+  std::string Code = "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";
+
+  EXPECT_THAT_ERROR(applyChange(Code, Change),
+                    FailedWithMessage("Line value is out of range (100)"));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang