aboutsummaryrefslogtreecommitdiff
path: root/clang/unittests/Tooling/RefactoringTest.cpp
diff options
context:
space:
mode:
authorEric Liu <ioeric@google.com>2017-03-01 13:14:01 +0000
committerEric Liu <ioeric@google.com>2017-03-01 13:14:01 +0000
commit9e745b729280d9d5f2a5e2871a0cdb40ed74cddd (patch)
tree2582de5d2fe64ee282952bd5eccf92be26fe8e4b /clang/unittests/Tooling/RefactoringTest.cpp
parent4a45efa431d13d017c8f4e0d99bfa37cd1993a72 (diff)
downloadllvm-9e745b729280d9d5f2a5e2871a0cdb40ed74cddd.zip
llvm-9e745b729280d9d5f2a5e2871a0cdb40ed74cddd.tar.gz
llvm-9e745b729280d9d5f2a5e2871a0cdb40ed74cddd.tar.bz2
Introducing clang::tooling::AtomicChange for refactoring tools.
Summary: An AtomicChange is used to create and group a set of source edits, e.g. replacements or header insertions. Edits in an AtomicChange should be related, e.g. replacements for the same type reference and the corresponding header insertion/deletion. An AtomicChange is uniquely identified by a key position and will either be fully applied or not applied at all. The key position should be the location of the key syntactical element that is being changed, e.g. the call to a refactored method. Next step: add a tool that applies AtomicChange. Reviewers: klimek, djasper Reviewed By: klimek Subscribers: alexshap, cfe-commits, djasper, mgorny Differential Revision: https://reviews.llvm.org/D27054 llvm-svn: 296616
Diffstat (limited to 'clang/unittests/Tooling/RefactoringTest.cpp')
-rw-r--r--clang/unittests/Tooling/RefactoringTest.cpp191
1 files changed, 187 insertions, 4 deletions
diff --git a/clang/unittests/Tooling/RefactoringTest.cpp b/clang/unittests/Tooling/RefactoringTest.cpp
index c29f8d7..41be7a8 100644
--- a/clang/unittests/Tooling/RefactoringTest.cpp
+++ b/clang/unittests/Tooling/RefactoringTest.cpp
@@ -26,6 +26,7 @@
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/AtomicChange.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/SmallString.h"
#include "gtest/gtest.h"
@@ -102,10 +103,10 @@ TEST_F(ReplacementTest, ReturnsInvalidPath) {
// Checks that an llvm::Error instance contains a ReplacementError with expected
// error code, expected new replacement, and expected existing replacement.
-static bool checkReplacementError(
- llvm::Error&& Error, replacement_error ExpectedErr,
- llvm::Optional<Replacement> ExpectedExisting,
- llvm::Optional<Replacement> ExpectedNew) {
+static bool checkReplacementError(llvm::Error &&Error,
+ replacement_error ExpectedErr,
+ llvm::Optional<Replacement> ExpectedExisting,
+ llvm::Optional<Replacement> ExpectedNew) {
if (!Error) {
llvm::errs() << "Error is a success.";
return false;
@@ -1089,5 +1090,187 @@ TEST(DeduplicateByFileTest, NonExistingFilePath) {
EXPECT_TRUE(FileToReplaces.empty());
}
+class AtomicChangeTest : public ::testing::Test {
+ protected:
+ void setUp() {
+ DefaultFileID = Context.createInMemoryFile("input.cpp", DefaultCode);
+ DefaultLoc = Context.Sources.getLocForStartOfFile(DefaultFileID)
+ .getLocWithOffset(20);
+ assert(DefaultLoc.isValid() && "Default location must be valid.");
+ }
+
+ RewriterTestContext Context;
+ std::string DefaultCode = std::string(100, 'a');
+ unsigned DefaultOffset = 20;
+ SourceLocation DefaultLoc;
+ FileID DefaultFileID;
+};
+
+TEST_F(AtomicChangeTest, AtomicChangeToYAML) {
+ setUp();
+ AtomicChange Change(Context.Sources, DefaultLoc);
+ llvm::Error Err =
+ Change.insert(Context.Sources, DefaultLoc, "aa", /*InsertAfter=*/false);
+ ASSERT_TRUE(!Err);
+ Err = Change.insert(Context.Sources, DefaultLoc.getLocWithOffset(10), "bb",
+ /*InsertAfter=*/false);
+ ASSERT_TRUE(!Err);
+ Change.addHeader("a.h");
+ Change.removeHeader("b.h");
+ std::string YAMLString = Change.toYAMLString();
+
+ // NOTE: If this test starts to fail for no obvious reason, check whitespace.
+ ASSERT_STREQ("---\n"
+ "Key: 'input.cpp:20'\n"
+ "FilePath: input.cpp\n"
+ "Error: ''\n"
+ "InsertedHeaders: [ a.h ]\n"
+ "RemovedHeaders: [ b.h ]\n"
+ "Replacements: \n" // Extra whitespace here!
+ " - FilePath: input.cpp\n"
+ " Offset: 20\n"
+ " Length: 0\n"
+ " ReplacementText: aa\n"
+ " - FilePath: input.cpp\n"
+ " Offset: 30\n"
+ " Length: 0\n"
+ " ReplacementText: bb\n"
+ "...\n",
+ YAMLString.c_str());
+}
+
+TEST_F(AtomicChangeTest, YAMLToAtomicChange) {
+ setUp();
+ std::string YamlContent = "---\n"
+ "Key: 'input.cpp:20'\n"
+ "FilePath: input.cpp\n"
+ "Error: 'ok'\n"
+ "InsertedHeaders: [ a.h ]\n"
+ "RemovedHeaders: [ b.h ]\n"
+ "Replacements: \n" // Extra whitespace here!
+ " - FilePath: input.cpp\n"
+ " Offset: 20\n"
+ " Length: 0\n"
+ " ReplacementText: aa\n"
+ " - FilePath: input.cpp\n"
+ " Offset: 30\n"
+ " Length: 0\n"
+ " ReplacementText: bb\n"
+ "...\n";
+ AtomicChange ExpectedChange(Context.Sources, DefaultLoc);
+ llvm::Error Err = ExpectedChange.insert(Context.Sources, DefaultLoc, "aa",
+ /*InsertAfter=*/false);
+ ASSERT_TRUE(!Err);
+ Err = ExpectedChange.insert(Context.Sources, DefaultLoc.getLocWithOffset(10),
+ "bb", /*InsertAfter=*/false);
+ ASSERT_TRUE(!Err);
+
+ ExpectedChange.addHeader("a.h");
+ ExpectedChange.removeHeader("b.h");
+ ExpectedChange.setError("ok");
+
+ AtomicChange ActualChange = AtomicChange::convertFromYAML(YamlContent);
+ EXPECT_EQ(ExpectedChange.getKey(), ActualChange.getKey());
+ EXPECT_EQ(ExpectedChange.getFilePath(), ActualChange.getFilePath());
+ EXPECT_EQ(ExpectedChange.getError(), ActualChange.getError());
+ EXPECT_EQ(ExpectedChange.getInsertedHeaders(), ActualChange.getInsertedHeaders());
+ EXPECT_EQ(ExpectedChange.getRemovedHeaders(), ActualChange.getRemovedHeaders());
+ EXPECT_EQ(ExpectedChange.getReplacements().size(),
+ ActualChange.getReplacements().size());
+ EXPECT_EQ(2u, ActualChange.getReplacements().size());
+ EXPECT_EQ(*ExpectedChange.getReplacements().begin(),
+ *ActualChange.getReplacements().begin());
+ EXPECT_EQ(*(++ExpectedChange.getReplacements().begin()),
+ *(++ActualChange.getReplacements().begin()));
+}
+
+TEST_F(AtomicChangeTest, CheckKeyAndKeyFile) {
+ setUp();
+ AtomicChange Change(Context.Sources, DefaultLoc);
+ EXPECT_EQ("input.cpp:20", Change.getKey());
+ EXPECT_EQ("input.cpp", Change.getFilePath());
+}
+
+TEST_F(AtomicChangeTest, InsertBefore) {
+ setUp();
+ AtomicChange Change(Context.Sources, DefaultLoc);
+ llvm::Error Err = Change.insert(Context.Sources, DefaultLoc, "aa");
+ ASSERT_TRUE(!Err);
+ EXPECT_EQ(Change.getReplacements().size(), 1u);
+ EXPECT_EQ(*Change.getReplacements().begin(),
+ Replacement(Context.Sources, DefaultLoc, 0, "aa"));
+ Err = Change.insert(Context.Sources, DefaultLoc, "b", /*InsertAfter=*/false);
+ ASSERT_TRUE(!Err);
+ EXPECT_EQ(Change.getReplacements().size(), 1u);
+ EXPECT_EQ(*Change.getReplacements().begin(),
+ Replacement(Context.Sources, DefaultLoc, 0, "baa"));
+}
+
+TEST_F(AtomicChangeTest, InsertAfter) {
+ setUp();
+ AtomicChange Change(Context.Sources, DefaultLoc);
+ llvm::Error Err = Change.insert(Context.Sources, DefaultLoc, "aa");
+ ASSERT_TRUE(!Err);
+ EXPECT_EQ(Change.getReplacements().size(), 1u);
+ EXPECT_EQ(*Change.getReplacements().begin(),
+ Replacement(Context.Sources, DefaultLoc, 0, "aa"));
+ Err = Change.insert(Context.Sources, DefaultLoc, "b");
+ ASSERT_TRUE(!Err);
+ EXPECT_EQ(Change.getReplacements().size(), 1u);
+ EXPECT_EQ(*Change.getReplacements().begin(),
+ Replacement(Context.Sources, DefaultLoc, 0, "aab"));
+}
+
+TEST_F(AtomicChangeTest, InsertBeforeWithInvalidLocation) {
+ setUp();
+ AtomicChange Change(Context.Sources, DefaultLoc);
+ llvm::Error Err =
+ Change.insert(Context.Sources, DefaultLoc, "a", /*InsertAfter=*/false);
+ ASSERT_TRUE(!Err);
+
+ // Invalid location.
+ Err = Change.insert(Context.Sources, SourceLocation(), "a",
+ /*InsertAfter=*/false);
+ ASSERT_TRUE((bool)Err);
+ EXPECT_TRUE(checkReplacementError(
+ std::move(Err), replacement_error::wrong_file_path,
+ Replacement(Context.Sources, DefaultLoc, 0, "a"),
+ Replacement(Context.Sources, SourceLocation(), 0, "a")));
+
+}
+
+TEST_F(AtomicChangeTest, InsertBeforeToWrongFile) {
+ setUp();
+ AtomicChange Change(Context.Sources, DefaultLoc);
+ llvm::Error Err =
+ Change.insert(Context.Sources, DefaultLoc, "a", /*InsertAfter=*/false);
+ ASSERT_TRUE(!Err);
+
+ // Inserting at a different file.
+ FileID NewID = Context.createInMemoryFile("extra.cpp", DefaultCode);
+ SourceLocation NewLoc = Context.Sources.getLocForStartOfFile(NewID);
+ Err = Change.insert(Context.Sources, NewLoc, "b", /*InsertAfter=*/false);
+ ASSERT_TRUE((bool)Err);
+ EXPECT_TRUE(
+ checkReplacementError(std::move(Err), replacement_error::wrong_file_path,
+ Replacement(Context.Sources, DefaultLoc, 0, "a"),
+ Replacement(Context.Sources, NewLoc, 0, "b")));
+}
+
+TEST_F(AtomicChangeTest, InsertAfterWithInvalidLocation) {
+ setUp();
+ AtomicChange Change(Context.Sources, DefaultLoc);
+ llvm::Error Err = Change.insert(Context.Sources, DefaultLoc, "a");
+ ASSERT_TRUE(!Err);
+
+ // Invalid location.
+ Err = Change.insert(Context.Sources, SourceLocation(), "b");
+ ASSERT_TRUE((bool)Err);
+ EXPECT_TRUE(checkReplacementError(
+ std::move(Err), replacement_error::wrong_file_path,
+ Replacement(Context.Sources, DefaultLoc, 0, "a"),
+ Replacement(Context.Sources, SourceLocation(), 0, "b")));
+}
+
} // end namespace tooling
} // end namespace clang