RefactoringActionRulesTest.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. //===- unittest/Tooling/RefactoringTestActionRulesTest.cpp ----------------===//
  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. #include "ReplacementTest.h"
  10. #include "RewriterTestContext.h"
  11. #include "clang/Tooling/Refactoring.h"
  12. #include "clang/Tooling/Refactoring/RefactoringActionRules.h"
  13. #include "clang/Tooling/Tooling.h"
  14. #include "llvm/Support/Errc.h"
  15. #include "gtest/gtest.h"
  16. using namespace clang;
  17. using namespace tooling;
  18. using namespace refactoring_action_rules;
  19. namespace {
  20. class RefactoringActionRulesTest : public ::testing::Test {
  21. protected:
  22. void SetUp() override {
  23. Context.Sources.setMainFileID(
  24. Context.createInMemoryFile("input.cpp", DefaultCode));
  25. }
  26. RewriterTestContext Context;
  27. std::string DefaultCode = std::string(100, 'a');
  28. };
  29. Expected<AtomicChanges>
  30. createReplacements(const std::unique_ptr<RefactoringActionRule> &Rule,
  31. RefactoringRuleContext &Context) {
  32. class Consumer final : public RefactoringResultConsumer {
  33. void handleError(llvm::Error Err) override { Result = std::move(Err); }
  34. void handle(AtomicChanges SourceReplacements) override {
  35. Result = std::move(SourceReplacements);
  36. }
  37. public:
  38. Optional<Expected<AtomicChanges>> Result;
  39. };
  40. Consumer C;
  41. Rule->invoke(C, Context);
  42. return std::move(*C.Result);
  43. }
  44. TEST_F(RefactoringActionRulesTest, MyFirstRefactoringRule) {
  45. auto ReplaceAWithB =
  46. [](std::pair<selection::SourceSelectionRange, int> Selection)
  47. -> Expected<AtomicChanges> {
  48. const SourceManager &SM = Selection.first.getSources();
  49. SourceLocation Loc = Selection.first.getRange().getBegin().getLocWithOffset(
  50. Selection.second);
  51. AtomicChange Change(SM, Loc);
  52. llvm::Error E = Change.replace(SM, Loc, 1, "b");
  53. if (E)
  54. return std::move(E);
  55. return AtomicChanges{Change};
  56. };
  57. class SelectionRequirement : public selection::Requirement {
  58. public:
  59. std::pair<selection::SourceSelectionRange, int>
  60. evaluateSelection(selection::SourceSelectionRange Selection) const {
  61. return std::make_pair(Selection, 20);
  62. }
  63. };
  64. auto Rule = createRefactoringRule(ReplaceAWithB,
  65. requiredSelection(SelectionRequirement()));
  66. // When the requirements are satisifed, the rule's function must be invoked.
  67. {
  68. RefactoringRuleContext RefContext(Context.Sources);
  69. SourceLocation Cursor =
  70. Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID())
  71. .getLocWithOffset(10);
  72. RefContext.setSelectionRange({Cursor, Cursor});
  73. Expected<AtomicChanges> ErrorOrResult =
  74. createReplacements(Rule, RefContext);
  75. ASSERT_FALSE(!ErrorOrResult);
  76. AtomicChanges Result = std::move(*ErrorOrResult);
  77. ASSERT_EQ(Result.size(), 1u);
  78. std::string YAMLString =
  79. const_cast<AtomicChange &>(Result[0]).toYAMLString();
  80. ASSERT_STREQ("---\n"
  81. "Key: 'input.cpp:30'\n"
  82. "FilePath: input.cpp\n"
  83. "Error: ''\n"
  84. "InsertedHeaders: \n"
  85. "RemovedHeaders: \n"
  86. "Replacements: \n" // Extra whitespace here!
  87. " - FilePath: input.cpp\n"
  88. " Offset: 30\n"
  89. " Length: 1\n"
  90. " ReplacementText: b\n"
  91. "...\n",
  92. YAMLString.c_str());
  93. }
  94. // When one of the requirements is not satisfied, invoke should return a
  95. // valid error.
  96. {
  97. RefactoringRuleContext RefContext(Context.Sources);
  98. Expected<AtomicChanges> ErrorOrResult =
  99. createReplacements(Rule, RefContext);
  100. ASSERT_TRUE(!ErrorOrResult);
  101. std::string Message;
  102. llvm::handleAllErrors(
  103. ErrorOrResult.takeError(),
  104. [&](llvm::StringError &Error) { Message = Error.getMessage(); });
  105. EXPECT_EQ(Message, "refactoring action can't be initiated with the "
  106. "specified selection range");
  107. }
  108. }
  109. TEST_F(RefactoringActionRulesTest, ReturnError) {
  110. Expected<AtomicChanges> (*Func)(selection::SourceSelectionRange) =
  111. [](selection::SourceSelectionRange) -> Expected<AtomicChanges> {
  112. return llvm::make_error<llvm::StringError>(
  113. "Error", llvm::make_error_code(llvm::errc::invalid_argument));
  114. };
  115. auto Rule = createRefactoringRule(
  116. Func, requiredSelection(
  117. selection::identity<selection::SourceSelectionRange>()));
  118. RefactoringRuleContext RefContext(Context.Sources);
  119. SourceLocation Cursor =
  120. Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID());
  121. RefContext.setSelectionRange({Cursor, Cursor});
  122. Expected<AtomicChanges> Result = createReplacements(Rule, RefContext);
  123. ASSERT_TRUE(!Result);
  124. std::string Message;
  125. llvm::handleAllErrors(Result.takeError(), [&](llvm::StringError &Error) {
  126. Message = Error.getMessage();
  127. });
  128. EXPECT_EQ(Message, "Error");
  129. }
  130. TEST_F(RefactoringActionRulesTest, ReturnInitiationDiagnostic) {
  131. RefactoringRuleContext RefContext(Context.Sources);
  132. class SelectionRequirement : public selection::Requirement {
  133. public:
  134. Expected<Optional<int>>
  135. evaluateSelection(selection::SourceSelectionRange Selection) const {
  136. return llvm::make_error<llvm::StringError>(
  137. "bad selection", llvm::make_error_code(llvm::errc::invalid_argument));
  138. }
  139. };
  140. auto Rule = createRefactoringRule(
  141. [](int) -> Expected<AtomicChanges> {
  142. llvm::report_fatal_error("Should not run!");
  143. },
  144. requiredSelection(SelectionRequirement()));
  145. SourceLocation Cursor =
  146. Context.Sources.getLocForStartOfFile(Context.Sources.getMainFileID());
  147. RefContext.setSelectionRange({Cursor, Cursor});
  148. Expected<AtomicChanges> Result = createReplacements(Rule, RefContext);
  149. ASSERT_TRUE(!Result);
  150. std::string Message;
  151. llvm::handleAllErrors(Result.takeError(), [&](llvm::StringError &Error) {
  152. Message = Error.getMessage();
  153. });
  154. EXPECT_EQ(Message, "bad selection");
  155. }
  156. } // end anonymous namespace