From f6b0ee3b398ae16cf1d8ec0b22dc11ac3965f473 Mon Sep 17 00:00:00 2001 From: parinaz-st Date: Fri, 17 Apr 2026 14:54:05 +0330 Subject: [PATCH 1/5] Study CodeTracker- Unit Tests Added case EXTRACT_FIXTURE Temporarily commented-out (not supported on RM 3.0.11) --- pom.xml | 19 +- .../AnnotationTrackerChangeHistory.java | 2 +- .../BlockTrackerChangeHistory.java | 2 +- .../CommentTrackerChangeHistory.java | 2 +- src/main/java/org/codetracker/Main.java | 224 ++++++++++-------- .../MethodTrackerChangeHistory.java | 2 +- .../VariableTrackerChangeHistory.java | 2 +- .../codetracker/AttributeTrackerImplTest.java | 58 +++++ src/test/java/org/codetracker/BlameTest.java | 122 ++++++++++ .../org/codetracker/BlockTrackerImplTest.java | 118 +++++++++ .../FalseRenameDetectionTests.java | 170 +++++++++++++ .../codetracker/MethodTrackerImplTest.java | 218 +++++++++++++++++ .../codetracker/VariableTrackerImplTest.java | 101 ++++++++ 13 files changed, 935 insertions(+), 105 deletions(-) create mode 100644 src/test/java/org/codetracker/AttributeTrackerImplTest.java create mode 100644 src/test/java/org/codetracker/BlameTest.java create mode 100644 src/test/java/org/codetracker/BlockTrackerImplTest.java create mode 100644 src/test/java/org/codetracker/FalseRenameDetectionTests.java create mode 100644 src/test/java/org/codetracker/MethodTrackerImplTest.java create mode 100644 src/test/java/org/codetracker/VariableTrackerImplTest.java diff --git a/pom.xml b/pom.xml index 9ad2dcfd0f6..9776d1d153e 100644 --- a/pom.xml +++ b/pom.xml @@ -40,10 +40,22 @@ refactoring-miner ${rm.version} + + + + + + - junit - junit - 4.13.2 + org.junit.jupiter + junit-jupiter-api + 5.8.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.8.2 test @@ -130,7 +142,6 @@ jackson-databind 2.17.1 - diff --git a/src/main/java/org/codetracker/AnnotationTrackerChangeHistory.java b/src/main/java/org/codetracker/AnnotationTrackerChangeHistory.java index a7f908e5abb..947b7eea6ab 100644 --- a/src/main/java/org/codetracker/AnnotationTrackerChangeHistory.java +++ b/src/main/java/org/codetracker/AnnotationTrackerChangeHistory.java @@ -140,7 +140,7 @@ public boolean checkForExtractionOrInline(Version currentVersion, Version parent } break; } - case EXTRACT_FIXTURE: +// case EXTRACT_FIXTURE:TODO: upgrade RM version then uncomment case MOVE_CODE: { MoveCodeRefactoring moveCodeRefactoring = (MoveCodeRefactoring) refactoring; Method extractedMethod = Method.of(moveCodeRefactoring.getTargetContainer(), currentVersion); diff --git a/src/main/java/org/codetracker/BlockTrackerChangeHistory.java b/src/main/java/org/codetracker/BlockTrackerChangeHistory.java index bf01484a079..8840829964c 100644 --- a/src/main/java/org/codetracker/BlockTrackerChangeHistory.java +++ b/src/main/java/org/codetracker/BlockTrackerChangeHistory.java @@ -441,7 +441,7 @@ else if (mapping instanceof LeafMapping && mapping.getFragment1() instanceof Sta break; } */ - case EXTRACT_FIXTURE: +// case EXTRACT_FIXTURE: TODO: upgrade RM version then uncomment case MOVE_CODE: { MoveCodeRefactoring moveCodeRefactoring = (MoveCodeRefactoring) refactoring; Method extractedMethod = Method.of(moveCodeRefactoring.getTargetContainer(), currentVersion); diff --git a/src/main/java/org/codetracker/CommentTrackerChangeHistory.java b/src/main/java/org/codetracker/CommentTrackerChangeHistory.java index 45c85d99b09..bb399aa000d 100644 --- a/src/main/java/org/codetracker/CommentTrackerChangeHistory.java +++ b/src/main/java/org/codetracker/CommentTrackerChangeHistory.java @@ -317,7 +317,7 @@ public boolean checkForExtractionOrInline(Version currentVersion, Version parent } break; } - case EXTRACT_FIXTURE: +// case EXTRACT_FIXTURE:TODO: upgrade RM version then uncomment case MOVE_CODE: { MoveCodeRefactoring moveCodeRefactoring = (MoveCodeRefactoring) refactoring; Method extractedMethod = Method.of(moveCodeRefactoring.getTargetContainer(), currentVersion); diff --git a/src/main/java/org/codetracker/Main.java b/src/main/java/org/codetracker/Main.java index 15293045808..86516fdde9d 100644 --- a/src/main/java/org/codetracker/Main.java +++ b/src/main/java/org/codetracker/Main.java @@ -16,51 +16,23 @@ import java.time.ZoneOffset; public class Main { - private final static String FOLDER_TO_CLONE = "tmp/"; +// private final static String FOLDER_TO_CLONE = "tmp/"; + private final static String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting"; + private final static String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git"; + public static void main(String args[]) throws Exception { GitService gitService = new GitServiceImpl(); - // BLOCK TRACKING EXAMPLE - try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle", - "https://github.com/checkstyle/checkstyle.git")){ - - BlockTracker blockTracker = CodeTracker.blockTracker() - .repository(repository) - .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java") - .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3") - .methodName("fireErrors") - .methodDeclarationLineNumber(384) - .codeElementType(CodeElementType.ENHANCED_FOR_STATEMENT) - .blockStartLineNumber(391) - .blockEndLineNumber(393) - .build(); - - History blockHistory = blockTracker.track(); - - for (History.HistoryInfo historyInfo : blockHistory.getHistoryInfoList()) { - System.out.println("======================================================"); - System.out.println("Commit ID: " + historyInfo.getCommitId()); - System.out.println("Date: " + - LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC)); - System.out.println("Before: " + historyInfo.getElementBefore().getName()); - System.out.println("After: " + historyInfo.getElementAfter().getName()); - - for (Change change : historyInfo.getChangeList()) { - System.out.println(change.getType().getTitle() + ": " + change); - } - } - System.out.println("======================================================"); - } // METHOD TRACKING EXAMPLE - try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle", - "https://github.com/checkstyle/checkstyle.git")){ + try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE)){ MethodTracker methodTracker = CodeTracker.methodTracker() .repository(repository) - .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java") - .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3") - .methodName("fireErrors") - .methodDeclarationLineNumber(384) + .filePath("CryptoServer/src/main/java/com/cryptography/service/CryptoService.java") + .startCommitId("d0d9edcfb611ee35978938f7e35250f1ae982057") + .methodName("ceateUser") + .methodDeclarationLineNumber(43) .build(); History methodHistory = methodTracker.track(); @@ -79,65 +51,125 @@ public static void main(String args[]) throws Exception { } System.out.println("======================================================"); } - // VARIABLE TRACKING EXAMPLE - try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle", - "https://github.com/checkstyle/checkstyle.git")){ - - VariableTracker variableTracker = CodeTracker.variableTracker() - .repository(repository) - .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java") - .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3") - .methodName("fireErrors") - .methodDeclarationLineNumber(384) - .variableName("stripped") - .variableDeclarationLineNumber(385) - .build(); - - History variableHistory = variableTracker.track(); - for (History.HistoryInfo historyInfo : variableHistory.getHistoryInfoList()) { - System.out.println("======================================================"); - System.out.println("Commit ID: " + historyInfo.getCommitId()); - System.out.println("Date: " + - LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC)); - System.out.println("Before: " + historyInfo.getElementBefore().getName()); - System.out.println("After: " + historyInfo.getElementAfter().getName()); - - for (Change change : historyInfo.getChangeList()) { - System.out.println(change.getType().getTitle() + ": " + change); - } - } - System.out.println("======================================================"); - } - - // ATTRIBUTE TRACKING EXAMPLE - try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle", - "https://github.com/checkstyle/checkstyle.git")) { - - AttributeTracker attributeTracker = CodeTracker.attributeTracker() - .repository(repository) - .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java") - .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3") - .attributeName("cacheFile") - .attributeDeclarationLineNumber(132) - .build(); - - History attributeHistory = attributeTracker.track(); - - for (History.HistoryInfo historyInfo : attributeHistory.getHistoryInfoList()) { - System.out.println("======================================================"); - System.out.println("Commit ID: " + historyInfo.getCommitId()); - System.out.println("Date: " + - LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC)); - System.out.println("Before: " + historyInfo.getElementBefore().getName()); - System.out.println("After: " + historyInfo.getElementAfter().getName()); - - for (Change change : historyInfo.getChangeList()) { - System.out.println(change.getType().getTitle() + ": " + change); - } - } - System.out.println("======================================================"); - } +// // BLOCK TRACKING EXAMPLE +// try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle", +// "https://github.com/checkstyle/checkstyle.git")){ +// +// BlockTracker blockTracker = CodeTracker.blockTracker() +// .repository(repository) +// .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java") +// .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3") +// .methodName("fireErrors") +// .methodDeclarationLineNumber(384) +// .codeElementType(CodeElementType.ENHANCED_FOR_STATEMENT) +// .blockStartLineNumber(391) +// .blockEndLineNumber(393) +// .build(); +// +// History blockHistory = blockTracker.track(); +// +// for (History.HistoryInfo historyInfo : blockHistory.getHistoryInfoList()) { +// System.out.println("======================================================"); +// System.out.println("Commit ID: " + historyInfo.getCommitId()); +// System.out.println("Date: " + +// LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC)); +// System.out.println("Before: " + historyInfo.getElementBefore().getName()); +// System.out.println("After: " + historyInfo.getElementAfter().getName()); +// +// for (Change change : historyInfo.getChangeList()) { +// System.out.println(change.getType().getTitle() + ": " + change); +// } +// } +// System.out.println("======================================================"); +// } +// // METHOD TRACKING EXAMPLE +// try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle", +// "https://github.com/checkstyle/checkstyle.git")){ +// +// MethodTracker methodTracker = CodeTracker.methodTracker() +// .repository(repository) +// .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java") +// .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3") +// .methodName("fireErrors") +// .methodDeclarationLineNumber(384) +// .build(); +// +// History methodHistory = methodTracker.track(); +// +// for (History.HistoryInfo historyInfo : methodHistory.getHistoryInfoList()) { +// System.out.println("======================================================"); +// System.out.println("Commit ID: " + historyInfo.getCommitId()); +// System.out.println("Date: " + +// LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC)); +// System.out.println("Before: " + historyInfo.getElementBefore().getName()); +// System.out.println("After: " + historyInfo.getElementAfter().getName()); +// +// for (Change change : historyInfo.getChangeList()) { +// System.out.println(change.getType().getTitle() + ": " + change); +// } +// } +// System.out.println("======================================================"); +// } +// // VARIABLE TRACKING EXAMPLE +// try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle", +// "https://github.com/checkstyle/checkstyle.git")){ +// +// VariableTracker variableTracker = CodeTracker.variableTracker() +// .repository(repository) +// .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java") +// .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3") +// .methodName("fireErrors") +// .methodDeclarationLineNumber(384) +// .variableName("stripped") +// .variableDeclarationLineNumber(385) +// .build(); +// +// History variableHistory = variableTracker.track(); +// +// for (History.HistoryInfo historyInfo : variableHistory.getHistoryInfoList()) { +// System.out.println("======================================================"); +// System.out.println("Commit ID: " + historyInfo.getCommitId()); +// System.out.println("Date: " + +// LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC)); +// System.out.println("Before: " + historyInfo.getElementBefore().getName()); +// System.out.println("After: " + historyInfo.getElementAfter().getName()); +// +// for (Change change : historyInfo.getChangeList()) { +// System.out.println(change.getType().getTitle() + ": " + change); +// } +// } +// System.out.println("======================================================"); +// } +// +// // ATTRIBUTE TRACKING EXAMPLE +// try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle", +// "https://github.com/checkstyle/checkstyle.git")) { +// +// AttributeTracker attributeTracker = CodeTracker.attributeTracker() +// .repository(repository) +// .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java") +// .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3") +// .attributeName("cacheFile") +// .attributeDeclarationLineNumber(132) +// .build(); +// +// History attributeHistory = attributeTracker.track(); +// +// for (History.HistoryInfo historyInfo : attributeHistory.getHistoryInfoList()) { +// System.out.println("======================================================"); +// System.out.println("Commit ID: " + historyInfo.getCommitId()); +// System.out.println("Date: " + +// LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC)); +// System.out.println("Before: " + historyInfo.getElementBefore().getName()); +// System.out.println("After: " + historyInfo.getElementAfter().getName()); +// +// for (Change change : historyInfo.getChangeList()) { +// System.out.println(change.getType().getTitle() + ": " + change); +// } +// } +// System.out.println("======================================================"); +// } } } diff --git a/src/main/java/org/codetracker/MethodTrackerChangeHistory.java b/src/main/java/org/codetracker/MethodTrackerChangeHistory.java index c87bb5ea8dd..5aa7e2f29da 100644 --- a/src/main/java/org/codetracker/MethodTrackerChangeHistory.java +++ b/src/main/java/org/codetracker/MethodTrackerChangeHistory.java @@ -448,7 +448,7 @@ else if (looserMapper(mapperWithLessMappings, mapper)) { } break; } - case EXTRACT_FIXTURE: +// case EXTRACT_FIXTURE: TODO: upgrade RM version then uncomment case MOVE_CODE: { MoveCodeRefactoring moveCodeRefactoring = (MoveCodeRefactoring) refactoring; operationBefore = moveCodeRefactoring.getSourceContainer(); diff --git a/src/main/java/org/codetracker/VariableTrackerChangeHistory.java b/src/main/java/org/codetracker/VariableTrackerChangeHistory.java index 848c6e7465f..4a0cbb52840 100644 --- a/src/main/java/org/codetracker/VariableTrackerChangeHistory.java +++ b/src/main/java/org/codetracker/VariableTrackerChangeHistory.java @@ -238,7 +238,7 @@ public boolean checkForExtractionOrInline(Version currentVersion, Version parent } break; } - case EXTRACT_FIXTURE: +// case EXTRACT_FIXTURE: TODO: upgrade RM version then uncomment case MOVE_CODE: { MoveCodeRefactoring moveCodeRefactoring = (MoveCodeRefactoring) refactoring; Method extractedMethod = Method.of(moveCodeRefactoring.getTargetContainer(), currentVersion); diff --git a/src/test/java/org/codetracker/AttributeTrackerImplTest.java b/src/test/java/org/codetracker/AttributeTrackerImplTest.java new file mode 100644 index 00000000000..aab4144716d --- /dev/null +++ b/src/test/java/org/codetracker/AttributeTrackerImplTest.java @@ -0,0 +1,58 @@ +package org.codetracker; + +import org.codetracker.api.AttributeTracker; +import org.codetracker.api.CodeTracker; +import org.codetracker.api.History; +import org.codetracker.change.Change; +import org.codetracker.element.Attribute; +import org.eclipse.jgit.lib.Repository; +import org.junit.jupiter.api.Test; +import org.refactoringminer.api.GitService; +import org.refactoringminer.util.GitServiceImpl; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class AttributeTrackerImplTest { + String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting"; + String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git"; + + @Test + public void returnFalseRename() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + + AttributeTracker attributeTracker = CodeTracker.attributeTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/genericcase/ServiceInfo.java") + .startCommitId("88cbdb012a2e92d029f20b5253272f32ce84c3be") + .attributeName("serviceName") + .attributeDeclarationLineNumber(3) + .build(); + History attributeHistory = attributeTracker.track(); + printHappyCaseOutput(attributeHistory); + assertNotNull(attributeHistory); + assertTrue(!attributeHistory.getHistoryInfoList().isEmpty() && attributeHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + + } + private void printHappyCaseOutput(History attributeHistory){ + for (History.HistoryInfo historyInfo : attributeHistory.getHistoryInfoList()) { + System.out.println("======================================================"); + System.out.println("Commit ID: " + historyInfo.getCommitId()); + System.out.println("Date: " + + LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC)); + System.out.println("Before: " + historyInfo.getElementBefore().getName()); + System.out.println("After: " + historyInfo.getElementAfter().getName()); + + for (Change change : historyInfo.getChangeList()) { + System.out.println(change.getType().getTitle() + ": " + change); + } + } + System.out.println("======================================================"); + } + + } diff --git a/src/test/java/org/codetracker/BlameTest.java b/src/test/java/org/codetracker/BlameTest.java new file mode 100644 index 00000000000..b32b60e109c --- /dev/null +++ b/src/test/java/org/codetracker/BlameTest.java @@ -0,0 +1,122 @@ +package org.codetracker; + +import gr.uom.java.xmi.LocationInfo; +import org.codetracker.api.BlockTracker; +import org.codetracker.api.CodeTracker; +import org.codetracker.api.History; +import org.codetracker.api.MethodTracker; +import org.codetracker.change.Change; +import org.codetracker.element.Block; +import org.codetracker.element.Method; +import org.eclipse.jgit.lib.Repository; +import org.junit.jupiter.api.Test; +import org.refactoringminer.api.GitService; +import org.refactoringminer.util.GitServiceImpl; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BlameTest { + String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting"; + String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git"; + + /*************************************************************************** + * ************************************* + * Blame + * False Rename Detection Instead Of Extract Class & Move Method + * ************************************* + */ + @Test + public void returnBlameWhenExtractClassNew() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/newclass/Calculator.java") + .startCommitId("8ef06dced725cca8cc22705d4eba95ef39ae1e36") + .methodName("print") + .methodDeclarationLineNumber(17) + .build(); + History.HistoryInfo methodHistory = methodTracker.blame(); + printHappyCaseOutputMethod(methodHistory); + assertNotNull(methodHistory); +// assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + } + + @Test + public void returnsBlameRenameMethodGivenCommitInfoOfRepository() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/service/CryptoService.java") + .startCommitId("d0d9edcfb611ee35978938f7e35250f1ae982057") + .methodName("ceateUser") + .methodDeclarationLineNumber(46) //54 43 + .build(); + History.HistoryInfo methodHistory = methodTracker.blame(); + printHappyCaseOutputMethod(methodHistory); + assertNotNull(methodHistory); +// assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().size() > 2); + } + + @Test + public void ifToTernaryOperatorTest() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + BlockTracker blockTracker = CodeTracker.blockTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/blocktracking/ternary/Main.java") + .startCommitId("3b7a3dfdbc8c9a6233a7d631bc9ff1eb35bc0653") + .methodName("isPassed") + .methodDeclarationLineNumber(5) + .codeElementType(LocationInfo.CodeElementType.RETURN_STATEMENT) + .blockStartLineNumber(6) + .blockEndLineNumber(6) + .blameLineNumber(6) + .build(); + + History.HistoryInfo blockHistory = blockTracker.blame(); + printHappyCaseOutputBlock(blockHistory); + assertNotNull(blockHistory); +// assertTrue(!blockHistory.getHistoryInfoList().isEmpty() && blockHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + + } + + private void printHappyCaseOutputBlock(History.HistoryInfo blockHistory) { + System.out.println("======================================================"); + System.out.println("Commit ID: " + blockHistory.getCommitId()); + System.out.println("Date: " + + LocalDateTime.ofEpochSecond(blockHistory.getCommitTime(), 0, ZoneOffset.UTC)); + System.out.println("CommitterName: " + blockHistory.getCommitterName()); + System.out.println("Before: " + blockHistory.getElementBefore().getName()); + System.out.println("After: " + blockHistory.getElementAfter().getName()); + + for (Change change : blockHistory.getChangeList()) { + System.out.println(change.getType().getTitle() + ": " + change); + } + System.out.println("======================================================"); + } + + private void printHappyCaseOutputMethod(History.HistoryInfo methodHistory) { + System.out.println("======================================================"); + System.out.println("Commit ID: " + methodHistory.getCommitId()); + System.out.println("Date: " + + LocalDateTime.ofEpochSecond(methodHistory.getCommitTime(), 0, ZoneOffset.UTC)); + System.out.println("CommitterName: " + methodHistory.getCommitterName()); + System.out.println("Before: " + methodHistory.getElementBefore().getName()); + System.out.println("After: " + methodHistory.getElementAfter().getName()); + + for (Change change : methodHistory.getChangeList()) { + System.out.println(change.getType().getTitle() + ": " + change); + } + System.out.println("======================================================"); + } + +} diff --git a/src/test/java/org/codetracker/BlockTrackerImplTest.java b/src/test/java/org/codetracker/BlockTrackerImplTest.java new file mode 100644 index 00000000000..98fe3746d20 --- /dev/null +++ b/src/test/java/org/codetracker/BlockTrackerImplTest.java @@ -0,0 +1,118 @@ +package org.codetracker; + +import gr.uom.java.xmi.LocationInfo; +import org.codetracker.api.BlockTracker; +import org.codetracker.api.CodeTracker; +import org.codetracker.api.History; +import org.codetracker.change.Change; +import org.codetracker.element.Block; +import org.eclipse.jgit.lib.Repository; +import org.junit.jupiter.api.Test; +import org.refactoringminer.api.GitService; +import org.refactoringminer.util.GitServiceImpl; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BlockTrackerImplTest { + String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting"; + String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git"; + @Test + public void ifToTernaryOperatorTest() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + BlockTracker blockTracker = CodeTracker.blockTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/blocktracking/ternary/Main.java") + .startCommitId("3b7a3dfdbc8c9a6233a7d631bc9ff1eb35bc0653") + .methodName("isPassed") + .methodDeclarationLineNumber(5) + .codeElementType(LocationInfo.CodeElementType.RETURN_STATEMENT) + .blockStartLineNumber(6) + .blockEndLineNumber(6) + .build(); + + History blockHistory = blockTracker.track(); + printHappyCaseOutput(blockHistory); + assertNotNull(blockHistory); + assertTrue(!blockHistory.getHistoryInfoList().isEmpty() && blockHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + + } + @Test + public void testDelegateMethodLocalFile() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + BlockTracker blockTracker = CodeTracker.blockTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/blocktracking/delegate/Main.java") + .startCommitId("97eb547f6fae7c09aba90f030f0557a77ee86cdf") + .methodName("getTheYoungestEmployeeSalary") + .methodDeclarationLineNumber(12) + .codeElementType(LocationInfo.CodeElementType.ENHANCED_FOR_STATEMENT) + .blockStartLineNumber(15) + .blockEndLineNumber(17) + .build(); + + History blockHistory = blockTracker.track(); + printHappyCaseOutput(blockHistory); + assertNotNull(blockHistory); + assertTrue(!blockHistory.getHistoryInfoList().isEmpty() && blockHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + } + @Test + public void testDelegateSameSignatureMethodOtherFile() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + BlockTracker blockTracker = CodeTracker.blockTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/blocktracking/delegate/Utilitu.java") + .startCommitId("f99f42b46810963f9e27973a28169734c5ee2ce3") + .methodName("getTheYoungestEmployeeSalary") + .methodDeclarationLineNumber(6) + .codeElementType(LocationInfo.CodeElementType.ENHANCED_FOR_STATEMENT) + .blockStartLineNumber(8) + .blockEndLineNumber(10) + .build(); + + History blockHistory = blockTracker.track(); + printHappyCaseOutput(blockHistory); + assertNotNull(blockHistory); + assertTrue(!blockHistory.getHistoryInfoList().isEmpty() && blockHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + } + private void printHappyCaseOutput(History blockHistory){ + for (History.HistoryInfo historyInfo : blockHistory.getHistoryInfoList()) { + System.out.println("======================================================"); + System.out.println("Commit ID: " + historyInfo.getCommitId()); + System.out.println("Date: " + + LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC)); + System.out.println("Before: " + historyInfo.getElementBefore().getName()); + System.out.println("After: " + historyInfo.getElementAfter().getName()); + + for (Change change : historyInfo.getChangeList()) { + System.out.println(change.getType().getTitle() + ": " + change); + } + } + System.out.println("======================================================"); + } + +} +// System.out.println("---- Statements visible to CodeTracker ----"); +// for (AbstractStatement st : +// startMethod.getUmlOperation() +// .getBody() +// .getCompositeStatement() +// .getAllStatements()) { +// +// System.out.printf("%s %d-%d%n", +// st.getLocationInfo().getCodeElementType(), +// st.getLocationInfo().getStartLine(), +// st.getLocationInfo().getEndLine()); +// } +// System.out.println("-------------------------------------------"); + + diff --git a/src/test/java/org/codetracker/FalseRenameDetectionTests.java b/src/test/java/org/codetracker/FalseRenameDetectionTests.java new file mode 100644 index 00000000000..3aa64101f68 --- /dev/null +++ b/src/test/java/org/codetracker/FalseRenameDetectionTests.java @@ -0,0 +1,170 @@ +package org.codetracker; + +import org.codetracker.api.CodeTracker; +import org.codetracker.api.History; +import org.codetracker.api.MethodTracker; +import org.codetracker.change.Change; +import org.codetracker.element.Method; +import org.eclipse.jgit.lib.Repository; +import org.junit.jupiter.api.Test; +import org.refactoringminer.api.GitService; +import org.refactoringminer.util.GitServiceImpl; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class FalseRenameDetectionTests { + String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting"; + String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git"; + /*************************************************************************** + * ************************************* + * False Rename Detection Instead Of Extract Class & Move Method + * ************************************* + */ + @Test + public void breakRMReturnRenameWhenExtractClassNew() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/newclass/Calculator.java") + .startCommitId("8ef06dced725cca8cc22705d4eba95ef39ae1e36") + .methodName("print") + .methodDeclarationLineNumber(15) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + } + @Test + public void breakRMReturnRenameWhenExtractClassExtend() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/extend/Calculator.java") + .startCommitId("a14ddd9082a29b53a0fe4b153f65b777fe122931") + .methodName("print") + .methodDeclarationLineNumber(20) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + } + @Test + public void breakRMReturnRenameWhenExtractClassFieldInjection() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/autowired/Calculator.java") + .startCommitId("0eae1091dc5ce1eddbd1edd62dcb12936ebb1978") + .methodName("print") + .methodDeclarationLineNumber(18) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + } + + + @Test + public void breakRMReturnRenameWhenExtractClassConstructionInjection() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/constructor/Calculator.java") + .startCommitId("ebe0ba262dc9c92473336baeb4d1d71df77f7d92") + .methodName("print") + .methodDeclarationLineNumber(19) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + + } + + @Test + public void breakRMReturnRenameWhenExtractClassSetterInjection() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/setter/Calculator.java") + .startCommitId("ea57744b29d0a8edf24348cf85db997f9192c12c") + .methodName("print") + .methodDeclarationLineNumber(20) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + + } + @Test + public void returnRenameWhenExtractClassStaticMethod() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/staticmethod/DiscountUtil.java") + .startCommitId("33bf82563170a549f8366271055a43ee1a53e13d") + .methodName("calculateDiscount") + .methodDeclarationLineNumber(4) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + + } + @Test + public void returnRenameWhenExtractClassFunctionalInterfaceSupplier() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/functionalinterface/Calculator.java") + .startCommitId("2df483b1cd01681f1cdc976ddc53d010b8e9cf23") + .methodName("sum") + .methodDeclarationLineNumber(7) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + + } + + private void printHappyCaseOutput(History methodHistory){ + for (History.HistoryInfo historyInfo : methodHistory.getHistoryInfoList()) { + System.out.println("======================================================"); + System.out.println("Commit ID: " + historyInfo.getCommitId()); + System.out.println("Date: " + + LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC)); + System.out.println("Before: " + historyInfo.getElementBefore().getName()); + System.out.println("After: " + historyInfo.getElementAfter().getName()); + + for (Change change : historyInfo.getChangeList()) { + System.out.println(change.getType().getTitle() + ": " + change); + } + } + System.out.println("======================================================"); + } + +} diff --git a/src/test/java/org/codetracker/MethodTrackerImplTest.java b/src/test/java/org/codetracker/MethodTrackerImplTest.java new file mode 100644 index 00000000000..659e7872a77 --- /dev/null +++ b/src/test/java/org/codetracker/MethodTrackerImplTest.java @@ -0,0 +1,218 @@ +package org.codetracker; + + +import org.codetracker.api.CodeTracker; +import org.codetracker.api.History; +import org.codetracker.api.MethodTracker; +import org.codetracker.change.Change; +import org.codetracker.element.Method; +import org.eclipse.jgit.lib.Repository; +import org.junit.jupiter.api.Test; +import org.refactoringminer.api.GitService; +import org.refactoringminer.util.GitServiceImpl; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MethodTrackerImplTest { + + String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting"; + String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git"; + + @Test + public void returnsHistoryOfRenameMethodGivenCommitInfoOfRepository() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/service/CryptoService.java") + .startCommitId("d0d9edcfb611ee35978938f7e35250f1ae982057") + .methodName("ceateUser") + .methodDeclarationLineNumber(43) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().size() > 2); + } + @Test + public void returnsHistoryOfMovedMethodContainerChanged() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/CryptographyApplication.java") + .startCommitId("8e0ada4854818a764d337d40c25f6060aa8ea1b2") + .methodName("main") + .methodDeclarationLineNumber(42) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change")); + } + @Test + public void returnNoChangeForBodyChangeDueToCollision() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/CryptographyApplication.java") + .startCommitId("5c08104df4a48e253714226c0d9c0d0f4e40ee30") + .methodName("run") + .methodDeclarationLineNumber(47) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("body change")); + } + @Test + public void returnContainerChangeForMovedFile() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/dto/request/DecryptReqDto.java") + .startCommitId("213ed6bbd3c0b94423e7338b191e26f876c0c0d5") + .methodName("getBase64OfCipherTest") + .methodDeclarationLineNumber(17) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change")); + + } +@Test +public void returnTheLastRefactoringTypeBeforeRenamingFile() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/dto/EncryptReqDto.java") + .startCommitId("213ed6bbd3c0b94423e7338b191e26f876c0c0d5") + .methodName("getPlainText") + .methodDeclarationLineNumber(6) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change")); + +} + @Test + public void returnContainerChangeAfterRenamingFile() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/dto/EncryptRequestDto.java") + .startCommitId("f8b244f015b79c95dfb7fb86340c749447f61b34") + .methodName("getPlainText") + .methodDeclarationLineNumber(6) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change")); + + } + @Test + public void testMoveFileAndBodyChangeSimultaneously() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/dto/Response/EncryptResDto.java") + .startCommitId("d64f222f3512c8ce5453d53c77093ae4b2a995ca") + .methodName("getCipherText") + .methodDeclarationLineNumber(6) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change")); + } + @Test + public void testConstructorWithMinimalChangeCommit() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/dto/UserDto.java") + .startCommitId("d64f222f3512c8ce5453d53c77093ae4b2a995ca") + .methodName("UserDto") + .methodDeclarationLineNumber(9) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change")); + + } + @Test + public void testMethodClassImplementsInterface() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/config/CustomUserDetailManagerImpl.java") + .startCommitId("d64f222f3512c8ce5453d53c77093ae4b2a995ca") + .methodName("createUser") + .methodDeclarationLineNumber(22) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change")); + } + @Test + public void uncommentedMethod() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + MethodTracker methodTracker = CodeTracker.methodTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/comment_to_method/Main.java") + .startCommitId("f1860ea8ac8f61b539b83236e6b7ca020e069cdb") + .methodName("goodbye") + .methodDeclarationLineNumber(7) + .build(); + History methodHistory = methodTracker.track(); + printHappyCaseOutput(methodHistory); + assertNotNull(methodHistory); + assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + + } + private void printHappyCaseOutput(History methodHistory){ + for (History.HistoryInfo historyInfo : methodHistory.getHistoryInfoList()) { + System.out.println("======================================================"); + System.out.println("Commit ID: " + historyInfo.getCommitId()); + System.out.println("Date: " + + LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC)); + System.out.println("CommitterName: " + historyInfo.getCommitterName()); + + System.out.println("Before: " + historyInfo.getElementBefore().getName()); + System.out.println("After: " + historyInfo.getElementAfter().getName()); + + for (Change change : historyInfo.getChangeList()) { + System.out.println(change.getType().getTitle() + ": " + change); + } + } + System.out.println("======================================================"); + } + +} diff --git a/src/test/java/org/codetracker/VariableTrackerImplTest.java b/src/test/java/org/codetracker/VariableTrackerImplTest.java new file mode 100644 index 00000000000..3628d2bba85 --- /dev/null +++ b/src/test/java/org/codetracker/VariableTrackerImplTest.java @@ -0,0 +1,101 @@ +package org.codetracker; + +import org.codetracker.api.CodeTracker; +import org.codetracker.api.History; +import org.codetracker.api.VariableTracker; +import org.codetracker.change.Change; +import org.codetracker.element.Variable; +import org.eclipse.jgit.lib.Repository; +import org.junit.jupiter.api.Test; +import org.refactoringminer.api.GitService; +import org.refactoringminer.util.GitServiceImpl; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class VariableTrackerImplTest { + String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting"; + String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git"; + @Test + public void testSimpleVariableTracker() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + + VariableTracker variableTracker = CodeTracker.variableTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/variabletracking/Main.java") + .startCommitId("6ad3db2a40834eba62b82c36aa07ab7e01c469d7") + .methodName("calculate") + .methodDeclarationLineNumber(5) + .variableName("num") + .variableDeclarationLineNumber(6) + .build(); + History variableHistory = variableTracker.track(); + printHappyCaseOutput(variableHistory); + assertNotNull(variableHistory); + assertTrue(!variableHistory.getHistoryInfoList().isEmpty() && variableHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + } + @Test + public void returnMultiVariableDeclarationRefactoring() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + + VariableTracker variableTracker = CodeTracker.variableTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/multiVariableDeclaration/Main.java") + .startCommitId("5bcc51064d77c323f4422009e66e2c6de93c86f1") + .methodName("main") + .methodDeclarationLineNumber(6) + .variableName("n") + .variableDeclarationLineNumber(8) + .build(); + History variableHistory = variableTracker.track(); + printHappyCaseOutput(variableHistory); + assertNotNull(variableHistory); + assertTrue(!variableHistory.getHistoryInfoList().isEmpty() && variableHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + + } + @Test + public void returnMultiVariableDeclarationSlideStatementRefactoring() throws Exception { + GitService gitService = new GitServiceImpl(); + Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE, + GITHUB_REPO_TO_CLONE); + + VariableTracker variableTracker = CodeTracker.variableTracker() + .repository(repository) + .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/multiVariableDeclaration/SlideStatement.java") + .startCommitId("b7a6cdde69b3af5247a1289ce34a3b5e5cff4593") + .methodName("main") + .methodDeclarationLineNumber(6) + .variableName("n") + .variableDeclarationLineNumber(9) + .build(); + History variableHistory = variableTracker.track(); + printHappyCaseOutput(variableHistory); + assertNotNull(variableHistory); + assertTrue(!variableHistory.getHistoryInfoList().isEmpty() && variableHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced")); + + } + public void printHappyCaseOutput(History variableHistory){ + for (History.HistoryInfo historyInfo : variableHistory.getHistoryInfoList()) { + System.out.println("======================================================"); + System.out.println("Commit ID: " + historyInfo.getCommitId()); + System.out.println("Date: " + + LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC)); + System.out.println("Before: " + historyInfo.getElementBefore().getName()); + System.out.println("After: " + historyInfo.getElementAfter().getName()); + + for (Change change : historyInfo.getChangeList()) { + System.out.println(change.getType().getTitle() + ": " + change); + } + } + System.out.println("======================================================"); + + } + +} From 0c9eb5ff17d3bab7941e37c1bf5b10ede446dc20 Mon Sep 17 00:00:00 2001 From: parinaz-st Date: Thu, 28 May 2026 19:20:49 +0330 Subject: [PATCH 2/5] Added support for uncommented Block detection, Comment statement transition detection for matched blocks --- .../BlockTrackerChangeHistory.java | 80 ++++--- .../java/org/codetracker/change/Change.java | 6 +- .../org/codetracker/change/ChangeFactory.java | 13 ++ .../change/block/CommentedOutCodeInBlock.java | 13 ++ .../change/block/UncommentedBlock.java | 13 ++ .../change/block/UncommentedCodeInBlock.java | 15 ++ .../util/CommentTransitionAnalyzer.java | 212 ++++++++++++++++++ 7 files changed, 317 insertions(+), 35 deletions(-) create mode 100644 src/main/java/org/codetracker/change/block/CommentedOutCodeInBlock.java create mode 100644 src/main/java/org/codetracker/change/block/UncommentedBlock.java create mode 100644 src/main/java/org/codetracker/change/block/UncommentedCodeInBlock.java create mode 100644 src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java diff --git a/src/main/java/org/codetracker/BlockTrackerChangeHistory.java b/src/main/java/org/codetracker/BlockTrackerChangeHistory.java index bf01484a079..d3f9e955cc1 100644 --- a/src/main/java/org/codetracker/BlockTrackerChangeHistory.java +++ b/src/main/java/org/codetracker/BlockTrackerChangeHistory.java @@ -2,17 +2,10 @@ import java.io.IOException; import java.io.StringReader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.function.Predicate; - +import gr.uom.java.xmi.*; +import gr.uom.java.xmi.diff.*; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.tuple.Pair; import org.codetracker.api.History; @@ -37,6 +30,7 @@ import org.codetracker.change.method.BodyChange; import org.codetracker.element.Block; import org.codetracker.element.Method; +import org.codetracker.util.Util; import org.refactoringminer.api.Refactoring; import com.github.difflib.DiffUtils; @@ -46,11 +40,7 @@ import com.github.difflib.patch.InsertDelta; import com.github.difflib.patch.Patch; -import gr.uom.java.xmi.UMLOperation; -import gr.uom.java.xmi.UMLType; -import gr.uom.java.xmi.VariableDeclarationContainer; import gr.uom.java.xmi.LocationInfo.CodeElementType; -import gr.uom.java.xmi.UMLAnonymousClass; import gr.uom.java.xmi.decomposition.AbstractCodeFragment; import gr.uom.java.xmi.decomposition.AbstractCodeMapping; import gr.uom.java.xmi.decomposition.AbstractExpression; @@ -63,23 +53,9 @@ import gr.uom.java.xmi.decomposition.TryStatementObject; import gr.uom.java.xmi.decomposition.UMLOperationBodyMapper; import gr.uom.java.xmi.decomposition.VariableDeclaration; -import gr.uom.java.xmi.diff.ExtractOperationRefactoring; -import gr.uom.java.xmi.diff.InlineOperationRefactoring; -import gr.uom.java.xmi.diff.MergeOperationRefactoring; -import gr.uom.java.xmi.diff.MoveCodeRefactoring; -import gr.uom.java.xmi.diff.MoveOperationRefactoring; -import gr.uom.java.xmi.diff.PullUpOperationRefactoring; -import gr.uom.java.xmi.diff.PushDownOperationRefactoring; -import gr.uom.java.xmi.diff.RenameOperationRefactoring; -import gr.uom.java.xmi.diff.ReplaceAnonymousWithClassRefactoring; -import gr.uom.java.xmi.diff.ReplaceAnonymousWithLambdaRefactoring; -import gr.uom.java.xmi.diff.ReplaceConditionalWithTernaryRefactoring; -import gr.uom.java.xmi.diff.ReplaceLoopWithPipelineRefactoring; -import gr.uom.java.xmi.diff.ReplacePipelineWithLoopRefactoring; -import gr.uom.java.xmi.diff.SplitConditionalRefactoring; -import gr.uom.java.xmi.diff.SplitOperationRefactoring; -import gr.uom.java.xmi.diff.UMLAbstractClassDiff; -import gr.uom.java.xmi.diff.UMLAnonymousClassDiff; + +import static org.codetracker.util.CommentTransitionAnalyzer.*; + public class BlockTrackerChangeHistory extends AbstractChangeHistory { private final ChangeHistory blockChangeHistory = new ChangeHistory<>(); @@ -693,9 +669,38 @@ public boolean checkBodyOfMatchedOperations(Version currentVersion, Version pare // check if it is in the matched if (isMatched(umlOperationBodyMapper, currentVersion, parentVersion, equalOperator)) return true; - //Check if is added + //check if block got uncommented + if(isBlockUncommented(umlOperationBodyMapper, currentVersion, parentVersion, equalOperator)){ + return true; + } +// Check if is added return isAdded(umlOperationBodyMapper, currentVersion, parentVersion, equalOperator); } + public boolean isBlockUncommented(UMLOperationBodyMapper umlOperationBodyMapper, Version currentVersion, Version parentVersion, Predicate equalOperator) { + List currentNonMappedInnerNodes = umlOperationBodyMapper.getNonMappedInnerNodesT2(); + UMLCommentListDiff umlCommentListDiff = umlOperationBodyMapper.getCommentListDiff(); + List deletedComments = umlCommentListDiff.getDeletedComments(); + if(currentNonMappedInnerNodes.size() == 0 || deletedComments == null || deletedComments.isEmpty()) + return false; + Map deletedCommentsHashMap = generateCommentTextHashMap(deletedComments); + for(CompositeStatementObject nonMappedInnerNode : currentNonMappedInnerNodes) { + String hashOfNonMappedInnerNode = Util.getSHA512(nonMappedInnerNode.getActualSignature()); + if (!deletedCommentsHashMap.containsKey(hashOfNonMappedInnerNode)) + continue; + Block blockAfter = Block.of(nonMappedInnerNode, umlOperationBodyMapper.getContainer2(), currentVersion); + if (!equalOperator.test(blockAfter)) + continue; + //check block body continuity + if (!isBlockBodyUnchangedDuringUncomment(umlOperationBodyMapper, nonMappedInnerNode, deletedCommentsHashMap, blockAfter)) + continue; + Block virtualBlockBefore = Block.of(nonMappedInnerNode, umlOperationBodyMapper.getContainer2(), parentVersion); // add metadata later, flag isVirtual = true; + blockChangeHistory.addChange(virtualBlockBefore, blockAfter, ChangeFactory.forBlock(Change.Type.UNCOMMENTED_BLOCK)); + elements.add(virtualBlockBefore); + blockChangeHistory.connectRelatedNodes(); + return true; + } + return false; + } public boolean isBlockRefactored(Collection refactorings, Version currentVersion, Version parentVersion, Predicate equalOperator) { Set leftBlockSet = analyseBlockRefactorings(refactorings, currentVersion, parentVersion, equalOperator); @@ -1137,7 +1142,16 @@ public boolean isMatched(UMLOperationBodyMapper umlOperationBodyMapper, Version List stringRepresentationBodyBefore = stringRepresentationBefore.subList(1, stringRepresentationBefore.size()); List stringRepresentationBodyAfter = stringRepresentationAfter.subList(1, stringRepresentationAfter.size()); if (!stringRepresentationBodyBefore.equals(stringRepresentationBodyAfter)) { - blockChangeHistory.addChange(blockBefore, blockAfter, ChangeFactory.forBlock(Change.Type.BODY_CHANGE)); + Set commentTransitions = DetectCommentTransitionsInsideMatchedBlock(umlOperationBodyMapper, blockBefore, blockAfter, currentVersion, parentVersion); + if(!commentTransitions.isEmpty()){ + for(Change.Type changeType : commentTransitions){ + blockChangeHistory.addChange(blockBefore, blockAfter, ChangeFactory.forBlock(changeType)); + } + } + else{ + blockChangeHistory.addChange(blockBefore, blockAfter, ChangeFactory.forBlock(Change.Type.BODY_CHANGE)); + } + } bodyChange = true; } diff --git a/src/main/java/org/codetracker/change/Change.java b/src/main/java/org/codetracker/change/Change.java index 0eca191d9b1..e3ffd031dd2 100644 --- a/src/main/java/org/codetracker/change/Change.java +++ b/src/main/java/org/codetracker/change/Change.java @@ -56,8 +56,10 @@ enum Type { INITIALIZER_ADDED("initializer added"), INITIALIZER_REMOVED("initializer removed"), SUPERCLASS_CHANGE("superclass change"), - INTERFACE_LIST_CHANGE("interface list change"); - + INTERFACE_LIST_CHANGE("interface list change"), + COMMENTED_OUT_STATEMENT("commented out statement"), + UNCOMMENTED_STATEMENT("uncommented statement"), + UNCOMMENTED_BLOCK("uncommented Block"); private static final Map lookup = new HashMap<>(); static { diff --git a/src/main/java/org/codetracker/change/ChangeFactory.java b/src/main/java/org/codetracker/change/ChangeFactory.java index 405b85aa30d..78cdf3ad153 100644 --- a/src/main/java/org/codetracker/change/ChangeFactory.java +++ b/src/main/java/org/codetracker/change/ChangeFactory.java @@ -141,6 +141,19 @@ public AbstractChange build() { change = new BodyChange(); break; } + case UNCOMMENTED_STATEMENT:{ + change = new UncommentedCodeInBlock(); + break; + } + case COMMENTED_OUT_STATEMENT:{ + change = new CommentedOutCodeInBlock(); + break; + } + case UNCOMMENTED_BLOCK:{ + change = new UncommentedBlock(); + break; + } + case CATCH_BLOCK_CHANGE: { change = new CatchBlockChange(); break; diff --git a/src/main/java/org/codetracker/change/block/CommentedOutCodeInBlock.java b/src/main/java/org/codetracker/change/block/CommentedOutCodeInBlock.java new file mode 100644 index 00000000000..f7469135110 --- /dev/null +++ b/src/main/java/org/codetracker/change/block/CommentedOutCodeInBlock.java @@ -0,0 +1,13 @@ +package org.codetracker.change.block; + +public class CommentedOutCodeInBlock extends BlockBodyChange { + public CommentedOutCodeInBlock() { + super(Type.COMMENTED_OUT_STATEMENT); + } + + @Override + public String toString() { + return "Body Change, Contains Commented-out Code"; + } + +} diff --git a/src/main/java/org/codetracker/change/block/UncommentedBlock.java b/src/main/java/org/codetracker/change/block/UncommentedBlock.java new file mode 100644 index 00000000000..df8c71c3620 --- /dev/null +++ b/src/main/java/org/codetracker/change/block/UncommentedBlock.java @@ -0,0 +1,13 @@ +package org.codetracker.change.block; + +public class UncommentedBlock extends BlockChange { + public UncommentedBlock() { + super(Type.UNCOMMENTED_BLOCK); + } + + @Override + public String toString() { + return "Block Change, Uncommented Block"; + } + +} diff --git a/src/main/java/org/codetracker/change/block/UncommentedCodeInBlock.java b/src/main/java/org/codetracker/change/block/UncommentedCodeInBlock.java new file mode 100644 index 00000000000..9f940f99931 --- /dev/null +++ b/src/main/java/org/codetracker/change/block/UncommentedCodeInBlock.java @@ -0,0 +1,15 @@ +package org.codetracker.change.block; + +import org.codetracker.change.Change; + +public class UncommentedCodeInBlock extends BlockBodyChange { + public UncommentedCodeInBlock() { + super(Change.Type.UNCOMMENTED_STATEMENT); + } + + @Override + public String toString() { + return "Body Change, Contains Uncommented Code"; + } + +} diff --git a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java new file mode 100644 index 00000000000..311466a9f48 --- /dev/null +++ b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java @@ -0,0 +1,212 @@ +package org.codetracker.util; + +import gr.uom.java.xmi.LocationInfo; +import gr.uom.java.xmi.UMLComment; +import gr.uom.java.xmi.decomposition.*; +import org.codetracker.api.Version; +import org.codetracker.change.Change; +import org.codetracker.element.Block; + +import java.util.*; + +public class CommentTransitionAnalyzer { + + public static Set DetectCommentTransitionsInsideMatchedBlock(UMLOperationBodyMapper umlOperationBodyMapper, Block blockBefore, Block blockAfter, Version currentVersion, Version parentVersion) { + Set changeTypes = new HashSet<>(); + List deletedComments = umlOperationBodyMapper.getCommentListDiff().getDeletedComments(); + List addedComments = umlOperationBodyMapper.getCommentListDiff().getAddedComments(); + Map deletedCommentsHash; + Map addedCommentsHash; + + if (!deletedComments.isEmpty()) { + deletedCommentsHash = generateCommentTextHashMap(deletedComments); + if (isUncommentedCodeDetected(deletedCommentsHash, umlOperationBodyMapper, blockAfter)) + changeTypes.add(Change.Type.UNCOMMENTED_STATEMENT); + } + if (!addedComments.isEmpty()) { + addedCommentsHash = generateCommentTextHashMap(addedComments); + if (isCommentedOutCodeDetected(addedCommentsHash, umlOperationBodyMapper, blockBefore)) { + changeTypes.add(Change.Type.COMMENTED_OUT_STATEMENT); + } + } + return changeTypes; + } + + private static boolean isUncommentedCodeDetected(Map deletedCommentsHash, UMLOperationBodyMapper umlOperationBodyMapper, Block blockAfter) { + if (deletedCommentsHash.isEmpty()) + return false; + // find unmatched leaf statements afterBlock in deleted comments + if (!umlOperationBodyMapper.getNonMappedLeavesT2().isEmpty()) { + for (AbstractCodeFragment abstractCodeFragment : umlOperationBodyMapper.getNonMappedLeavesT2()) { + if (blockAfter.getComposite().getLeaves().contains(abstractCodeFragment)) { //if nonmappedLeave actually belongs to the target Block + String codeFragmentHash = Util.getSHA512(abstractCodeFragment.getString().trim()); + if (deletedCommentsHash.containsKey(codeFragmentHash)) { + return true; + } + } + } + } + //find unmatched composite statements afterBlock in deleted comments + if (!umlOperationBodyMapper.getNonMappedInnerNodesT2().isEmpty()) { + for (CompositeStatementObject compositeStatementObject : umlOperationBodyMapper.getNonMappedInnerNodesT2()) { + if (((CompositeStatementObject) blockAfter.getComposite()).contains(compositeStatementObject)) { //if nonMappedInnerNode actually belongs to the current block + String codeFragmentHash = Util.getSHA512(compositeStatementObject.getActualSignature()); + if (deletedCommentsHash.containsKey(codeFragmentHash)) { + return true; + } + } + } + } + // RefactoringMiner may create leaf mappings between semantically different statements when textual similarity is high enough, + // even when one side is actually a commented/uncommented transition. + if (!umlOperationBodyMapper.getMappings().isEmpty()) { + for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) { + if (!(mapping instanceof LeafMapping)) // does not happen in composite(i guess) + continue; + if (!isMappingBelongToTrackedBlock(mapping, blockAfter, true)) + continue; + //this issue only happens with leaf statements and not composites.... + String codeFragmentHashCurrent = Util.getSHA512(mapping.getFragment2().getString().trim()); + if (deletedCommentsHash.containsKey(codeFragmentHashCurrent)) { + return true; + } + } + } + return false; + } + + private static boolean isCommentedOutCodeDetected(Map addedCommentsHash, UMLOperationBodyMapper umlOperationBodyMapper, Block blockBefore) { + if (addedCommentsHash.isEmpty()) + return false; + if (!umlOperationBodyMapper.getNonMappedLeavesT1().isEmpty()) { + //find unmatched leaf statements parent1 in added comments + for (AbstractCodeFragment abstractCodeFragment : umlOperationBodyMapper.getNonMappedLeavesT1()) { + if (blockBefore.getComposite().getLeaves().contains(abstractCodeFragment)) { // if nonMappedleaf actually belongs to parent block + String codeFragmentHash = Util.getSHA512(abstractCodeFragment.getString().trim()); + if (addedCommentsHash.containsKey(codeFragmentHash)) { + return true; + } + } + } + } + if (!umlOperationBodyMapper.getNonMappedInnerNodesT1().isEmpty()) { + //find unmatched composite statements parent in added comments + for (CompositeStatementObject compositeStatementObject : umlOperationBodyMapper.getNonMappedInnerNodesT1()) { + if (((CompositeStatementObject) blockBefore.getComposite()).contains(compositeStatementObject)) { // if nonMappedInnerNode actually belongs to parent block + if (!isBracketAndBelongsToStandAloneBlock(compositeStatementObject)) + continue; + String codeFragmentHash = Util.getSHA512(compositeStatementObject.getActualSignature()); + if (addedCommentsHash.containsKey(codeFragmentHash)) { + return true; + } + } + } + } + // RefactoringMiner may create leaf mappings between semantically different statements when textual similarity is high enough, + // even when one side is actually a commented/uncommented transition. + if (!umlOperationBodyMapper.getMappings().isEmpty()) { + for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) { + if (!(mapping instanceof LeafMapping)) + continue; + if (!isMappingBelongToTrackedBlock(mapping, blockBefore, false)) + continue; + String codeFragmentHashParent = Util.getSHA512(mapping.getFragment1().getString().trim()); + if (!addedCommentsHash.isEmpty() && addedCommentsHash.containsKey(codeFragmentHashParent)) { + return true; + } + } + } + return false; + } + public static boolean isBlockBodyUnchangedDuringUncomment (UMLOperationBodyMapper umlOperationBodyMapper, CompositeStatementObject uncommentedBlock, Map deletedComments, Block blockAfter) { + Set mappedFragments = new HashSet<>(); + for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) { + if(!isMappingBelongToTrackedBlock(mapping, blockAfter, true)) + continue; + if(mapping instanceof CompositeStatementObjectMapping) { + CompositeStatementObject composite = (CompositeStatementObject) mapping.getFragment2(); + if (!isBracketAndBelongsToStandAloneBlock(composite)) + continue; + mappedFragments.add(Util.getSHA512(composite.getActualSignature())); + } + else if(mapping instanceof LeafMapping && mapping.getFragment2() instanceof StatementObject){ // mappings belong to the currentblock + mappedFragments.add(Util.getSHA512(mapping.getFragment2().getString().trim())); + } + } + for (CompositeStatementObject inner : uncommentedBlock.getInnerNodes()) { + if (!isBracketAndBelongsToStandAloneBlock(inner)) { + continue; + } + String hash = Util.getSHA512(inner.getActualSignature().trim()); + if(!existsAsMappedOrComment(hash, mappedFragments, deletedComments)){ + return false; + } + } + for (AbstractCodeFragment leaf : uncommentedBlock.getLeaves()) { + String hash = Util.getSHA512(((StatementObject) leaf).getActualSignature()); + if(!existsAsMappedOrComment(hash, mappedFragments, deletedComments)) + return false; + } + return true; + } + + private static Change.Type partialBlockUncommented(CompositeStatementObject compositeStatementObject, Block blockBefore, Block blockAfter) { + //TODO: else/catch/finally uncommented support should be added + if (blockBefore.getComposite().getLocationInfo().getCodeElementType().equals(LocationInfo.CodeElementType.IF_STATEMENT) && + blockAfter.getComposite().getLocationInfo().getCodeElementType().equals(LocationInfo.CodeElementType.IF_STATEMENT)) { + CompositeStatementObject ifBefore = (CompositeStatementObject) blockBefore.getComposite(); + CompositeStatementObject ifAfter = (CompositeStatementObject) blockAfter.getComposite(); + if (ifBefore.getStatements().size() == 1 && ifAfter.getStatements().size() == 2) { + return Change.Type.UNCOMMENTED_STATEMENT; + } + } + return null; + } + + public static boolean isBracketAndBelongsToStandAloneBlock(CompositeStatementObject compositeStatementObject){ //heuristic + if ((compositeStatementObject.getLocationInfo().getCodeElementType() == LocationInfo.CodeElementType.BLOCK) && (!compositeStatementObject.getParent().getActualSignature().equals("{"))) + return false; + return true; + } + private static boolean isMappingBelongToTrackedBlock(AbstractCodeMapping mapping, Block block, boolean isAfter){ + if(mapping instanceof CompositeStatementObjectMapping){ + CompositeStatementObject composite = (CompositeStatementObject) mapping.getFragment2(); + if(!((CompositeStatementObject) block.getComposite()).contains(composite)) + return false; + } + else if(mapping instanceof LeafMapping && (isAfter ? mapping.getFragment2() : mapping.getFragment1()) instanceof StatementObject){ + if(!block.getComposite().getLeaves().contains(isAfter ? mapping.getFragment2() : mapping.getFragment1())) + return false; + } + return true; + } + private static boolean existsAsMappedOrComment(String hash, Set mappedFragments, Map comments){ + return mappedFragments.contains(hash) || comments.containsKey(hash); + } + public static String normalizeCommentText(String commentText) { + if (commentText.startsWith("//")) { + commentText = commentText.substring(2); + } + commentText = commentText.trim(); + return commentText; + } + + public static Map generateCommentTextHashMap(List commentList) { + Map commentTextHashMap = new HashMap<>(); + for (UMLComment comment : commentList) { + String text = comment.getText(); + // remove /* */ + text = text.replace("/*", "").replace("*/", ""); + String[] lines = text.split("\\R"); + for (String line : lines) { + line = normalizeCommentText(line); + if (line.isBlank()) { + continue; + } + commentTextHashMap.put(Util.getSHA512(line), comment); + } + } + return commentTextHashMap; + } + +} From 2fda0750c3ba1d954edac10a7a548b6860cce873 Mon Sep 17 00:00:00 2001 From: parinaz-st Date: Fri, 5 Jun 2026 21:49:52 +0330 Subject: [PATCH 3/5] - Added scope boundary checking for comments - Removed heuristic for handling comments in similar matched statements --- .../util/CommentTransitionAnalyzer.java | 51 +++++++------------ 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java index 311466a9f48..a9872a470fa 100644 --- a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java +++ b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java @@ -19,19 +19,33 @@ public static Set DetectCommentTransitionsInsideMatchedBlock(UMLOpe Map addedCommentsHash; if (!deletedComments.isEmpty()) { - deletedCommentsHash = generateCommentTextHashMap(deletedComments); - if (isUncommentedCodeDetected(deletedCommentsHash, umlOperationBodyMapper, blockAfter)) + List inScopeDeletedComments; + inScopeDeletedComments = removeOutOfScopeComments(deletedComments, blockBefore); + deletedCommentsHash = generateCommentTextHashMap(inScopeDeletedComments); + if (isUncommentedCodeDetected(deletedCommentsHash, umlOperationBodyMapper, blockAfter)) { changeTypes.add(Change.Type.UNCOMMENTED_STATEMENT); + } } if (!addedComments.isEmpty()) { - addedCommentsHash = generateCommentTextHashMap(addedComments); + List inScopeAddedComments; + inScopeAddedComments = removeOutOfScopeComments(addedComments, blockAfter); + addedCommentsHash = generateCommentTextHashMap(inScopeAddedComments); if (isCommentedOutCodeDetected(addedCommentsHash, umlOperationBodyMapper, blockBefore)) { changeTypes.add(Change.Type.COMMENTED_OUT_STATEMENT); } } return changeTypes; } - + private static List removeOutOfScopeComments(List commentList, Block block){ + List inScopeComments = new ArrayList<>(); + for (int i = 0; i < commentList.size(); i++) { + if ((commentList.get(i).getLocationInfo().getStartLine() > block.getLocation().getStartLine() && + commentList.get(i).getLocationInfo().getEndLine() < block.getLocation().getEndLine())) { + inScopeComments.add(commentList.get(i)); + } + } + return inScopeComments; + } private static boolean isUncommentedCodeDetected(Map deletedCommentsHash, UMLOperationBodyMapper umlOperationBodyMapper, Block blockAfter) { if (deletedCommentsHash.isEmpty()) return false; @@ -57,21 +71,6 @@ private static boolean isUncommentedCodeDetected(Map deleted } } } - // RefactoringMiner may create leaf mappings between semantically different statements when textual similarity is high enough, - // even when one side is actually a commented/uncommented transition. - if (!umlOperationBodyMapper.getMappings().isEmpty()) { - for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) { - if (!(mapping instanceof LeafMapping)) // does not happen in composite(i guess) - continue; - if (!isMappingBelongToTrackedBlock(mapping, blockAfter, true)) - continue; - //this issue only happens with leaf statements and not composites.... - String codeFragmentHashCurrent = Util.getSHA512(mapping.getFragment2().getString().trim()); - if (deletedCommentsHash.containsKey(codeFragmentHashCurrent)) { - return true; - } - } - } return false; } @@ -102,20 +101,6 @@ private static boolean isCommentedOutCodeDetected(Map addedC } } } - // RefactoringMiner may create leaf mappings between semantically different statements when textual similarity is high enough, - // even when one side is actually a commented/uncommented transition. - if (!umlOperationBodyMapper.getMappings().isEmpty()) { - for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) { - if (!(mapping instanceof LeafMapping)) - continue; - if (!isMappingBelongToTrackedBlock(mapping, blockBefore, false)) - continue; - String codeFragmentHashParent = Util.getSHA512(mapping.getFragment1().getString().trim()); - if (!addedCommentsHash.isEmpty() && addedCommentsHash.containsKey(codeFragmentHashParent)) { - return true; - } - } - } return false; } public static boolean isBlockBodyUnchangedDuringUncomment (UMLOperationBodyMapper umlOperationBodyMapper, CompositeStatementObject uncommentedBlock, Map deletedComments, Block blockAfter) { From 7e3fd6015eb7ee88faafa92cd237aa9119fe8a13 Mon Sep 17 00:00:00 2001 From: parinaz-st Date: Fri, 12 Jun 2026 14:57:41 +0330 Subject: [PATCH 4/5] Refine the comment-transition detection heuristic by applying it only to leaf mappings with replacements --- .../util/CommentTransitionAnalyzer.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java index a9872a470fa..b8a6474cf05 100644 --- a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java +++ b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java @@ -71,6 +71,22 @@ private static boolean isUncommentedCodeDetected(Map deleted } } } + // RefactoringMiner may create leaf mappings between semantically different statements when textual similarity is high enough, + // even when one side is actually a commented/uncommented transition. + if (!umlOperationBodyMapper.getMappings().isEmpty()) { + for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) { + if (!(mapping instanceof LeafMapping)) // does not happen in composite + continue; + if(mapping.getReplacements().isEmpty()) //real mapping + continue; + if (!isMappingBelongToTrackedBlock(mapping, blockAfter, true)) + continue; + String codeFragmentHashCurrent = Util.getSHA512(mapping.getFragment2().getString().trim()); + if (deletedCommentsHash.containsKey(codeFragmentHashCurrent)) { + return true; + } + } + } return false; } @@ -101,6 +117,22 @@ private static boolean isCommentedOutCodeDetected(Map addedC } } } + // RefactoringMiner may create leaf mappings between semantically different statements when textual similarity is high enough, + // even when one side is actually a commented/uncommented transition. + if (!umlOperationBodyMapper.getMappings().isEmpty()) { + for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) { + if (!(mapping instanceof LeafMapping)) // does not happen in composite + continue; + if(mapping.getReplacements().isEmpty()) //real mapping + continue; + if (!isMappingBelongToTrackedBlock(mapping, blockBefore, false)) + continue; + String codeFragmentHashParent = Util.getSHA512(mapping.getFragment1().getString().trim()); + if (!addedCommentsHash.isEmpty() && addedCommentsHash.containsKey(codeFragmentHashParent)) { + return true; + } + } + } return false; } public static boolean isBlockBodyUnchangedDuringUncomment (UMLOperationBodyMapper umlOperationBodyMapper, CompositeStatementObject uncommentedBlock, Map deletedComments, Block blockAfter) { From 7c8d874576a099e510e972bbde9e54c1470afc0b Mon Sep 17 00:00:00 2001 From: parinaz-st Date: Thu, 9 Jul 2026 00:45:58 +0330 Subject: [PATCH 5/5] Support detection of multiline commented-out block signatures --- .../BlockTrackerChangeHistory.java | 17 +- .../util/CommentTransitionAnalyzer.java | 182 +++++++++++++++++- 2 files changed, 185 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/codetracker/BlockTrackerChangeHistory.java b/src/main/java/org/codetracker/BlockTrackerChangeHistory.java index 2743ff53f90..10d97298bcb 100644 --- a/src/main/java/org/codetracker/BlockTrackerChangeHistory.java +++ b/src/main/java/org/codetracker/BlockTrackerChangeHistory.java @@ -682,22 +682,27 @@ public boolean isBlockUncommented(UMLOperationBodyMapper umlOperationBodyMapper, List deletedComments = umlCommentListDiff.getDeletedComments(); if(currentNonMappedInnerNodes.size() == 0 || deletedComments == null || deletedComments.isEmpty()) return false; - Map deletedCommentsHashMap = generateCommentTextHashMap(deletedComments); + //reconstruct multiline block signature + Map allDeletedCommentsHashMap = generateCommentTextHashMap(deletedComments); + Map deletedCommentsBlockSignatureHashMap = generateUnCommentedBlockSignaturetHashMap(deletedComments); + if(deletedCommentsBlockSignatureHashMap.isEmpty()) + return false; for(CompositeStatementObject nonMappedInnerNode : currentNonMappedInnerNodes) { - String hashOfNonMappedInnerNode = Util.getSHA512(nonMappedInnerNode.getActualSignature()); - if (!deletedCommentsHashMap.containsKey(hashOfNonMappedInnerNode)) + String normalizedSignature = nonMappedInnerNode.getActualSignature().replaceAll("\\s+", " ").trim(); + String hashOfNonMappedInnerNode = Util.getSHA512(normalizedSignature); + if (!deletedCommentsBlockSignatureHashMap.containsKey(hashOfNonMappedInnerNode)) continue; Block blockAfter = Block.of(nonMappedInnerNode, umlOperationBodyMapper.getContainer2(), currentVersion); if (!equalOperator.test(blockAfter)) continue; //check block body continuity - if (!isBlockBodyUnchangedDuringUncomment(umlOperationBodyMapper, nonMappedInnerNode, deletedCommentsHashMap, blockAfter)) + if (!isBlockBodyUnchangedDuringUncomment(umlOperationBodyMapper, nonMappedInnerNode, allDeletedCommentsHashMap, blockAfter)) continue; Block virtualBlockBefore = Block.of(nonMappedInnerNode, umlOperationBodyMapper.getContainer2(), parentVersion); // add metadata later, flag isVirtual = true; blockChangeHistory.addChange(virtualBlockBefore, blockAfter, ChangeFactory.forBlock(Change.Type.UNCOMMENTED_BLOCK)); elements.add(virtualBlockBefore); blockChangeHistory.connectRelatedNodes(); - return true; + return true; } return false; } @@ -1142,7 +1147,7 @@ public boolean isMatched(UMLOperationBodyMapper umlOperationBodyMapper, Version List stringRepresentationBodyBefore = stringRepresentationBefore.subList(1, stringRepresentationBefore.size()); List stringRepresentationBodyAfter = stringRepresentationAfter.subList(1, stringRepresentationAfter.size()); if (!stringRepresentationBodyBefore.equals(stringRepresentationBodyAfter)) { - Set commentTransitions = DetectCommentTransitionsInsideMatchedBlock(umlOperationBodyMapper, blockBefore, blockAfter, currentVersion, parentVersion); + Set commentTransitions = detectCommentTransitionsInsideMatchedBlock(umlOperationBodyMapper, blockBefore, blockAfter, currentVersion, parentVersion); if(!commentTransitions.isEmpty()){ for(Change.Type changeType : commentTransitions){ blockChangeHistory.addChange(blockBefore, blockAfter, ChangeFactory.forBlock(changeType)); diff --git a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java index b8a6474cf05..e33c4941e63 100644 --- a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java +++ b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java @@ -11,7 +11,7 @@ public class CommentTransitionAnalyzer { - public static Set DetectCommentTransitionsInsideMatchedBlock(UMLOperationBodyMapper umlOperationBodyMapper, Block blockBefore, Block blockAfter, Version currentVersion, Version parentVersion) { + public static Set detectCommentTransitionsInsideMatchedBlock(UMLOperationBodyMapper umlOperationBodyMapper, Block blockBefore, Block blockAfter, Version currentVersion, Version parentVersion) { Set changeTypes = new HashSet<>(); List deletedComments = umlOperationBodyMapper.getCommentListDiff().getDeletedComments(); List addedComments = umlOperationBodyMapper.getCommentListDiff().getAddedComments(); @@ -151,16 +151,19 @@ else if(mapping instanceof LeafMapping && mapping.getFragment2() instanceof Stat } } for (CompositeStatementObject inner : uncommentedBlock.getInnerNodes()) { + if(inner.getActualSignature().equals(uncommentedBlock.getActualSignature())){ + continue; + } if (!isBracketAndBelongsToStandAloneBlock(inner)) { continue; } - String hash = Util.getSHA512(inner.getActualSignature().trim()); + String hash = Util.getSHA512(inner.getActualSignature().replaceAll("\\s+", " ").trim()); if(!existsAsMappedOrComment(hash, mappedFragments, deletedComments)){ return false; } } for (AbstractCodeFragment leaf : uncommentedBlock.getLeaves()) { - String hash = Util.getSHA512(((StatementObject) leaf).getActualSignature()); + String hash = Util.getSHA512(((StatementObject) leaf).getActualSignature().replaceAll("\\s+", " ").trim()); if(!existsAsMappedOrComment(hash, mappedFragments, deletedComments)) return false; } @@ -197,23 +200,35 @@ else if(mapping instanceof LeafMapping && (isAfter ? mapping.getFragment2() : ma } return true; } - private static boolean existsAsMappedOrComment(String hash, Set mappedFragments, Map comments){ - return mappedFragments.contains(hash) || comments.containsKey(hash); + private static boolean existsAsMappedOrComment(String hash, Set mappedFragments, Map commentsHash){ + return mappedFragments.contains(hash) || commentsHash.containsKey(hash); } public static String normalizeCommentText(String commentText) { + commentText = commentText.trim(); if (commentText.startsWith("//")) { commentText = commentText.substring(2); } + if (commentText.startsWith("/*")) { + commentText = commentText.substring(2); + } + if (commentText.endsWith("*/")) { + commentText = commentText.substring(0, commentText.length() - 2); + } commentText = commentText.trim(); - return commentText; + if (commentText.startsWith("*")) { + commentText = commentText.substring(1); + } + return commentText.trim(); } public static Map generateCommentTextHashMap(List commentList) { Map commentTextHashMap = new HashMap<>(); for (UMLComment comment : commentList) { String text = comment.getText(); - // remove /* */ - text = text.replace("/*", "").replace("*/", ""); + text = normalizeCommentText(text); + if(comment.getFullText().startsWith("/*")){ + commentTextHashMap.put(Util.getSHA512(text.replaceAll("\\s+", " ").trim()), comment); + } String[] lines = text.split("\\R"); for (String line : lines) { line = normalizeCommentText(line); @@ -225,5 +240,156 @@ public static Map generateCommentTextHashMap(List generateUnCommentedBlockSignaturetHashMap(List commentList) { + Map blockSignatureUnCommentedHashMap = new HashMap<>(); + List blockSignatureUnCommented = extractBlockSignaturesFromComments(commentList); + for(String signature : blockSignatureUnCommented){ + String normalized = signature.replaceAll("\\s+", " ").trim(); + blockSignatureUnCommentedHashMap.put(Util.getSHA512(normalized), normalized); + } + return blockSignatureUnCommentedHashMap; + } +public static List extractBlockSignaturesFromComments(List comments) { + List signatures = new ArrayList<>(); + + for (int i = 0; i < comments.size(); i++) { + UMLComment comment = comments.get(i); + String text = normalizeCommentText(comment.getText()); + if (text.isBlank()) + continue; + if (isLeafStatement(text)) + continue; + // Complete one-line signature + if (isCompleteBlockSignature(text)) { + signatures.add(text); + continue; + } + if (comment.getFullText().startsWith("/*")) { + signatures.addAll(extractFromBlockComment(text)); + } + else if (comment.getFullText().startsWith("//")) { + if (!hasBlockKeyword(text)) + continue; + SingleLineExtractionResult singleLineExtractionResult = extractFromSingleLineComments(comments, i, text); + signatures.addAll(singleLineExtractionResult.getSignatures()); + i = singleLineExtractionResult.getLastConsumedIndex(); + } + } + return signatures; +} +private static boolean isCompleteBlockSignature(String text) { + return hasBlockKeyword(text) && text.trim().endsWith("{"); + } + private static boolean signatureEnds(String text) { + return text.trim().endsWith("{"); + } + private static boolean hasBlockKeyword(String text) { + String trimmed = text.trim(); + return trimmed.startsWith("if") + || trimmed.startsWith("else") + || trimmed.startsWith("for") + || trimmed.startsWith("while") + || trimmed.startsWith("do") + || trimmed.startsWith("try") + || trimmed.startsWith("catch") + || trimmed.startsWith("finally") + || trimmed.startsWith("switch") + || trimmed.startsWith("synchronized") + || trimmed.startsWith("static"); + //TODO:Add support for standalone block {} + } + private static boolean areConsecutiveComments( + UMLComment current, + UMLComment next) { + + return next.getLocationInfo().getStartLine() + == current.getLocationInfo().getEndLine() + 1; + } + private static boolean isLeafStatement(String text) { + return (!hasBlockKeyword(text) && text.trim().endsWith(";")); + } + + private static List extractFromBlockComment(String commentText) { + List signatures = new ArrayList<>(); + String[] lines = commentText.split("\\R"); + for (int j = 0; j < lines.length; j++) { + if (isLeafStatement(lines[j])) + continue; + if (!hasBlockKeyword(lines[j])) + continue; + StringBuilder signatureBuilder = new StringBuilder(); + signatureBuilder.append(lines[j]); + int currentIndex = j + 1; + while (!signatureEnds(signatureBuilder.toString()) + && currentIndex < lines.length) { + String nextLine = lines[currentIndex]; + if (isLeafStatement(nextLine)) { + if (signatureBuilder.toString().trim().endsWith(")")) { + signatures.add(signatureBuilder.toString()); + } + break; + } + // another block signature starts + if (hasBlockKeyword(nextLine)) + break; + signatureBuilder.append("\n").append(nextLine); + currentIndex++; + } + if (signatureEnds(signatureBuilder.toString())) { + signatures.add(signatureBuilder.toString()); + } + j = currentIndex - 1; + } + return signatures; + } + + private static SingleLineExtractionResult extractFromSingleLineComments(List comments, int index, String commentText){ + int lastConsumedIndex = index; + List signatures = new ArrayList<>(); + StringBuilder signatureBuilder = new StringBuilder(); + signatureBuilder.append(commentText); + int currentIndex = index; + while (!signatureEnds(signatureBuilder.toString()) + && currentIndex + 1 < comments.size()) { + UMLComment current = comments.get(currentIndex); + UMLComment next = comments.get(currentIndex + 1); + if (!areConsecutiveComments(current, next)) + break; + String nextText = normalizeCommentText(next.getText()); + if (isLeafStatement(nextText)) { + if (signatureBuilder.toString().trim().endsWith(")")) { + signatures.add(signatureBuilder.toString()); + } + break; + } + // another block signature starts inside the same comment + if (hasBlockKeyword(nextText)) + break; + signatureBuilder.append("\n").append(nextText); + currentIndex++; + } + if (signatureEnds(signatureBuilder.toString())) { + signatures.add(signatureBuilder.toString()); + lastConsumedIndex = currentIndex; + } + return new SingleLineExtractionResult(signatures, lastConsumedIndex); + } + private static class SingleLineExtractionResult { + private List signatures; + private int lastConsumedIndex; + SingleLineExtractionResult(List signatures, + int lastConsumedIndex) { + this.signatures = signatures; + this.lastConsumedIndex = lastConsumedIndex; + } + public List getSignatures() { + return signatures; + } + + public int getLastConsumedIndex() { + return lastConsumedIndex; + } + } } +