-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implementing Mining Framework for S3M handlers analysis
- Loading branch information
1 parent
cc1d58d
commit 6900788
Showing
16 changed files
with
855 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package services.S3MHandlersAnalysis | ||
|
||
enum Handlers { | ||
Renaming | ||
|
||
static final Map<Integer, String> mergeResultPaths = [0: 'textual.java', 1: 'Renaming/CT.java', 2: 'Renaming/SF.java', 3: 'Renaming/MM.java', 4: 'Renaming/KB.java'] | ||
static final Map<Integer, String> mergeAlgorithms = [0: 'TM', 1: 'CT', 2: 'SF', 3: 'MM', 4: 'KB'] | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package services.S3MHandlersAnalysis | ||
|
||
@Grab('com.google.inject:guice:4.2.2') | ||
import com.google.inject.AbstractModule | ||
import com.google.inject.multibindings.Multibinder | ||
import main.interfaces.DataCollector | ||
import services.S3MHandlersAnalysis.implementations.CommitFilter | ||
import services.S3MHandlersAnalysis.implementations.MergesCollector | ||
import services.S3MHandlersAnalysis.implementations.OutputProcessor | ||
import services.S3MHandlersAnalysis.implementations.ProjectProcessor | ||
|
||
class MiningModule extends AbstractModule { | ||
|
||
@Override | ||
protected void configure() { | ||
Multibinder<DataCollector> dataCollectorBinder = Multibinder.newSetBinder(binder(), DataCollector.class) | ||
|
||
dataCollectorBinder.addBinding().to(MergesCollector.class) | ||
|
||
bind(main.interfaces.CommitFilter.class).to(CommitFilter.class) | ||
bind(main.interfaces.ProjectProcessor.class).to(ProjectProcessor.class) | ||
bind(main.interfaces.OutputProcessor.class).to(OutputProcessor.class) | ||
} | ||
|
||
} |
39 changes: 39 additions & 0 deletions
39
src/services/S3MHandlersAnalysis/datacollection/DataAnalyser.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package services.S3MHandlersAnalysis.datacollection | ||
|
||
import main.project.MergeCommit | ||
import main.project.Project | ||
import services.S3MHandlersAnalysis.Handlers | ||
import services.S3MHandlersAnalysis.util.BuildRequester | ||
import services.S3MHandlersAnalysis.util.MergeCommitSummary | ||
import services.S3MHandlersAnalysis.util.MergeScenarioSummary | ||
|
||
import java.nio.file.Path | ||
|
||
class DataAnalyser { | ||
|
||
static MergeCommitSummary analyseScenarios(Project project, MergeCommit mergeCommit, List<Path> mergeScenarios) { | ||
MergeCommitSummary summary = new MergeCommitSummary() | ||
buildCommitSummary(summary, mergeScenarios) | ||
|
||
checkForFalseNegatives(project, mergeCommit, mergeScenarios, summary) | ||
return summary | ||
} | ||
|
||
private static void buildCommitSummary(MergeCommitSummary summary, List<Path> mergeScenarios) { | ||
mergeScenarios.stream() | ||
.map(MergeScenarioSummary::new) | ||
.forEach(summary::addMergeSummary) | ||
} | ||
|
||
private static void checkForFalseNegatives(Project project, MergeCommit mergeCommit, List<Path> mergeScenarios, MergeCommitSummary summary) { | ||
summary.numberOfConflicts.eachWithIndex { int numConflicts, int i -> | ||
if (numConflicts == 0 && !summary.handlersHaveSameConflicts) { | ||
// there's a merge result with at least one conflict | ||
String buildLink = BuildRequester.requestBuildWithRevision(project, mergeCommit, mergeScenarios, i) | ||
summary.markAsChecking(buildLink, Handlers.mergeAlgorithms[i]) | ||
println 'Requested Travis build' | ||
} | ||
} | ||
} | ||
|
||
} |
76 changes: 76 additions & 0 deletions
76
src/services/S3MHandlersAnalysis/datacollection/MergeScenarioCollector.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package services.S3MHandlersAnalysis.datacollection | ||
|
||
import main.project.MergeCommit | ||
import main.project.Project | ||
import main.util.ProcessRunner | ||
import services.S3MHandlersAnalysis.util.Utils | ||
|
||
import java.nio.file.Files | ||
import java.nio.file.Path | ||
import java.util.stream.Collectors | ||
|
||
class MergeScenarioCollector { | ||
|
||
static List<Path> collectMergeScenarios(Project project, MergeCommit mergeCommit) { | ||
return getModifiedJavaFiles(project, mergeCommit).stream() | ||
.map(modifiedFile -> storeAndRetrieveMergeQuadruple(project, mergeCommit, modifiedFile)) | ||
.map(quadruple -> quadruple.getV4().getParent()) | ||
.collect(Collectors.toList()) | ||
} | ||
|
||
private static Tuple4<Path, Path, Path, Path> storeAndRetrieveMergeQuadruple(Project project, MergeCommit mergeCommit, String modifiedFile) { | ||
Path leftFile = storeFile(project, mergeCommit, modifiedFile, mergeCommit.getLeftSHA(), 'left') | ||
Path baseFile = storeFile(project, mergeCommit, modifiedFile, mergeCommit.getAncestorSHA(), 'base') | ||
Path rightFile = storeFile(project, mergeCommit, modifiedFile, mergeCommit.getRightSHA(), 'right') | ||
Path mergeFile = storeFile(project, mergeCommit, modifiedFile, mergeCommit.getSHA(), 'merge') | ||
return new Tuple4(leftFile, baseFile, rightFile, mergeFile) | ||
} | ||
|
||
private static Path storeFile(Project project, MergeCommit mergeCommit, String modifiedFile, String commitSHA, String fileName) { | ||
Path mergeScenarioDirectory = Utils.commitFilesPath(project, mergeCommit).resolve(modifiedFile) | ||
createDirectories(mergeScenarioDirectory) | ||
|
||
Path filePath = mergeScenarioDirectory.resolve("${fileName}.java") | ||
Files.deleteIfExists(filePath) | ||
filePath.toFile() << getFileContent(project, modifiedFile, commitSHA) | ||
return filePath | ||
} | ||
|
||
private static String getFileContent(Project project, String modifiedFile, String commitSHA) { | ||
StringBuilder fileContent = new StringBuilder() | ||
|
||
Process gitShow = ProcessRunner.runProcess(project.getPath(), "git", "show", "${commitSHA}:${modifiedFile}") | ||
gitShow.getInputStream().eachLine { | ||
fileContent.append(it).append('\n') | ||
} | ||
return fileContent.toString() | ||
} | ||
|
||
private static List<String> getModifiedJavaFiles(Project project, MergeCommit mergeCommit) { | ||
Process gitDiffTree = ProcessRunner.runProcess(project.getPath(), "git", "diff-tree", "--no-commit-id", "--name-status", "-r", mergeCommit.getSHA(), mergeCommit.getAncestorSHA()) | ||
List<String> modifiedFiles = gitDiffTree.getInputStream().readLines() | ||
|
||
return modifiedFiles.stream() | ||
.filter(MergeScenarioCollector::isModifiedFile) | ||
.filter(MergeScenarioCollector::isJavaFile) | ||
.map(MergeScenarioCollector::getPath) | ||
.collect(Collectors.toList()) | ||
} | ||
|
||
private static boolean isModifiedFile(String line) { | ||
return line.charAt(0) == 'M' as char | ||
} | ||
|
||
private static boolean isJavaFile(String line) { | ||
return line.endsWith('.java') | ||
} | ||
|
||
private static String getPath(String line) { | ||
return line.substring(1).trim() | ||
} | ||
|
||
private static void createDirectories(Path path) { | ||
path.toFile().mkdirs() | ||
} | ||
|
||
} |
78 changes: 78 additions & 0 deletions
78
src/services/S3MHandlersAnalysis/datacollection/S3MRunner.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package services.S3MHandlersAnalysis.datacollection | ||
|
||
import main.util.ProcessRunner | ||
import services.S3MHandlersAnalysis.Handlers | ||
|
||
import java.nio.file.Files | ||
import java.nio.file.Path | ||
import java.nio.file.Paths | ||
import java.nio.file.StandardCopyOption | ||
|
||
class S3MRunner { | ||
|
||
static final Path S3M_PATH = Paths.get("src/services/S3MHandlersAnalysis/s3m.jar") | ||
|
||
static void collectS3MResults(List<Path> mergeScenarios, List<Handlers> handlers) { | ||
mergeScenarios.parallelStream() | ||
.forEach(mergeScenario -> runHandlerVariants(mergeScenario, handlers)) | ||
} | ||
|
||
private static void runHandlerVariants(Path mergeScenario, List<Handlers> handlers) { | ||
Path leftFile = getInvolvedFile(mergeScenario, 'left') | ||
Path baseFile = getInvolvedFile(mergeScenario, 'base') | ||
Path rightFile = getInvolvedFile(mergeScenario, 'right') | ||
|
||
// To extend the analysis for other handlers, clone and modify the following conditional. | ||
if (handlers.contains(Handlers.Renaming)) { | ||
runS3M(leftFile, baseFile, rightFile, 'CT.java', Handlers.Renaming, '-hmcrdov') | ||
runS3M(leftFile, baseFile, rightFile, 'SF.java', Handlers.Renaming, '-r', 'SAFE') | ||
runS3M(leftFile, baseFile, rightFile, 'MM.java', Handlers.Renaming, '-r', 'MERGE') | ||
runS3M(leftFile, baseFile, rightFile, 'KB.java', Handlers.Renaming, '-r', 'BOTH') | ||
} | ||
} | ||
|
||
private static void runS3M(Path leftFile, Path baseFile, Path rightFile, String outputFileName, Handlers handler, String... additionalParameters) { | ||
Process S3M = ProcessRunner.startProcess(buildS3MProcess(leftFile, baseFile, rightFile, outputFileName, handler, additionalParameters)) | ||
S3M.getInputStream().eachLine { | ||
//println it | ||
} | ||
S3M.waitFor() | ||
|
||
renameUnstructuredMergeFile(baseFile.getParent(), handler.name(), outputFileName) | ||
} | ||
|
||
private static void renameUnstructuredMergeFile(Path mergeScenario, String handlerName, String outputFileName) { | ||
Path currentUnstructuredMergeFile = mergeScenario.resolve(handlerName).resolve("${outputFileName}.merge") | ||
Path renamedUnstructuredMergeFile = mergeScenario.resolve("textual.java") | ||
Files.move(currentUnstructuredMergeFile, renamedUnstructuredMergeFile, StandardCopyOption.REPLACE_EXISTING) | ||
} | ||
|
||
private static ProcessBuilder buildS3MProcess(Path leftFile, Path baseFile, Path rightFile, String outputFileName, Handlers handler, String... additionalParameters) { | ||
ProcessBuilder S3M = ProcessRunner.buildProcess(getParentAsString(S3M_PATH)) | ||
List<String> parameters = buildS3MParameters(leftFile, baseFile, rightFile, outputFileName, handler.name(), additionalParameters) | ||
S3M.command().addAll(parameters) | ||
return S3M | ||
} | ||
|
||
private static List<String> buildS3MParameters(Path leftFile, Path baseFile, Path rightFile, String outputFileName, String handlerName, String... additionalParameters) { | ||
List<String> parameters = ['java', '-jar', getNameAsString(S3M_PATH), leftFile.toString(), baseFile.toString(), rightFile.toString(), '-o', getOutputPath(baseFile.getParent(), handlerName, outputFileName).toString(), '-c', 'false', '-l', 'false'] | ||
parameters.addAll(additionalParameters.toList()) | ||
return parameters | ||
} | ||
|
||
private static Path getOutputPath(Path mergeScenario, String handlerName, String fileName) { | ||
return mergeScenario.resolve(handlerName).resolve(fileName) | ||
} | ||
|
||
private static String getParentAsString(Path path) { | ||
return path.getParent().toString() | ||
} | ||
|
||
private static String getNameAsString(Path path) { | ||
return path.getFileName().toString() | ||
} | ||
|
||
private static Path getInvolvedFile(Path mergeScenario, String fileName) { | ||
return mergeScenario.resolve("${fileName}.java").toAbsolutePath() | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
src/services/S3MHandlersAnalysis/datacollection/SpreadsheetBuilder.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package services.S3MHandlersAnalysis.datacollection | ||
|
||
import main.project.MergeCommit | ||
import main.project.Project | ||
import services.S3MHandlersAnalysis.implementations.OutputProcessor | ||
import services.S3MHandlersAnalysis.util.MergeCommitSummary | ||
import services.S3MHandlersAnalysis.util.MergeScenarioSummary | ||
import services.S3MHandlersAnalysis.util.Utils | ||
|
||
import java.nio.file.Path | ||
|
||
class SpreadsheetBuilder { | ||
private static final String GLOBAL_SPREADSHEET_HEADER = 'project,merge commit,number of modified files,number of TM conflicts,number of CT conflicts,number of SF conflicts,number of MM conflicts,number of KB conflicts,handlers have the same outputs,handlers have the same conflicts,notes,false positives,false negatives,travis builds,,,' | ||
private static final String COMMIT_SPREADSHEET_HEADER = 'project,merge commit,file,number of TM conflicts,number of CT conflicts,number of SF conflicts,number of MM conflicts,number of KB conflicts,CT text = SF text,CT text = MM text,CT text = KB text,SF text = MM text, SF text = KB text,MM text = KB text,CT conflicts = SF conflicts,CT conflicts = MM conflicts,CT conflicts = KB conflicts,SF conflicts = MM conflicts,SF conflicts = KB conflicts,MM conflicts = KB conflicts' | ||
private static final String SPREADSHEET_NAME = 'results.csv' | ||
|
||
static synchronized void buildSpreadsheets(Project project, MergeCommit mergeCommit, MergeCommitSummary summary) { | ||
buildGlobalSpreadsheet(project, mergeCommit, summary) | ||
buildCommitSpreadsheet(project, mergeCommit, summary.mergeScenarioSummaries) | ||
} | ||
|
||
private static void buildCommitSpreadsheet(Project project, MergeCommit mergeCommit, List<MergeScenarioSummary> summaries) { | ||
Path spreadsheetPath = Utils.commitFilesPath(project, mergeCommit).resolve(SPREADSHEET_NAME) | ||
File spreadsheet = spreadsheetPath.toFile() | ||
appendHeader(spreadsheet, COMMIT_SPREADSHEET_HEADER) | ||
|
||
summaries.each { summary -> | ||
appendLineToSpreadsheet(spreadsheet, appendAfterProjectAndMergeCommitLinks(project, mergeCommit, summary.toString())) | ||
} | ||
} | ||
|
||
private static void buildGlobalSpreadsheet(Project project, MergeCommit mergeCommit, MergeCommitSummary summary) { | ||
Path spreadsheetPath = Utils.getOutputPath().resolve(SPREADSHEET_NAME) | ||
File spreadsheet = spreadsheetPath.toFile() | ||
appendHeader(spreadsheet, GLOBAL_SPREADSHEET_HEADER) | ||
|
||
appendLineToSpreadsheet(spreadsheet, appendAfterProjectAndMergeCommitLinks(project, mergeCommit, summary.toString())) | ||
} | ||
|
||
private static void appendHeader(File spreadsheet, String header) { | ||
if (!spreadsheet.exists()) { | ||
appendLineToSpreadsheet(spreadsheet, header) | ||
} | ||
} | ||
|
||
private static String appendAfterProjectAndMergeCommitLinks(Project project, MergeCommit mergeCommit, String string) { | ||
String projectName = Utils.getHyperLink(OutputProcessor.ANALYSIS_REMOTE_URL + "/${project.getName()}", project.getName()) | ||
String commitSHA = Utils.getHyperLink(OutputProcessor.ANALYSIS_REMOTE_URL + "/${project.getName()}/${mergeCommit.getSHA()}", mergeCommit.getSHA()) | ||
return "${projectName},${commitSHA},${string}" | ||
} | ||
|
||
private static void appendLineToSpreadsheet(File spreadsheet, String line) { | ||
spreadsheet << "${line.replaceAll('\\\\', '/')}\n" | ||
} | ||
|
||
} |
24 changes: 24 additions & 0 deletions
24
src/services/S3MHandlersAnalysis/implementations/CommitFilter.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package services.S3MHandlersAnalysis.implementations | ||
|
||
import main.project.MergeCommit | ||
import main.project.Project | ||
import main.util.ProcessRunner | ||
import services.S3MHandlersAnalysis.datacollection.MergeScenarioCollector | ||
|
||
class CommitFilter implements main.interfaces.CommitFilter { | ||
|
||
@Override | ||
boolean applyFilter(Project project, MergeCommit mergeCommit) { | ||
return thereIsAtLeastOneMergeScenario(project, mergeCommit) | ||
} | ||
|
||
private static boolean thereIsAtLeastOneMergeScenario(Project project, MergeCommit mergeCommit) { | ||
Process gitDiffTree = ProcessRunner.runProcess(project.getPath(), "git", "diff-tree", "--no-commit-id", "--name-status", "-r", mergeCommit.getSHA(), mergeCommit.getAncestorSHA()) | ||
List<String> modifiedFiles = gitDiffTree.getInputStream().readLines() | ||
|
||
return modifiedFiles.stream() | ||
.filter(MergeScenarioCollector::isModifiedFile) | ||
.filter(MergeScenarioCollector::isJavaFile) | ||
.count() > 0 | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
src/services/S3MHandlersAnalysis/implementations/MergesCollector.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package services.S3MHandlersAnalysis.implementations | ||
|
||
import main.interfaces.DataCollector | ||
import main.project.MergeCommit | ||
import main.project.Project | ||
import services.S3MHandlersAnalysis.datacollection.DataAnalyser | ||
import services.S3MHandlersAnalysis.datacollection.MergeScenarioCollector | ||
import services.S3MHandlersAnalysis.datacollection.SpreadsheetBuilder | ||
import services.S3MHandlersAnalysis.util.MergeCommitSummary | ||
|
||
import java.nio.file.Path | ||
|
||
class MergesCollector implements DataCollector { | ||
// groovy -cp src src/main/app/MiningFramework.groovy -a b22d2fc334ece38945974c789654e8f56d812b02 -i services.S3MHandlersAnalysis.MiningModule projects.csv | ||
|
||
@Override | ||
void collectData(Project project, MergeCommit mergeCommit) { | ||
List<Path> mergeScenarios = MergeScenarioCollector.collectMergeScenarios(project, mergeCommit) | ||
println 'Collected merge scenarios' | ||
|
||
// S3MRunner.collectS3MResults(mergeScenarios, [Handlers.Renaming]) | ||
// println 'Collected S3M results' | ||
|
||
MergeCommitSummary summary = DataAnalyser.analyseScenarios(project, mergeCommit, mergeScenarios) | ||
println 'Summarized collected data' | ||
|
||
SpreadsheetBuilder.buildSpreadsheets(project, mergeCommit, summary) | ||
println 'Built spreadsheets' | ||
} | ||
|
||
} |
31 changes: 31 additions & 0 deletions
31
src/services/S3MHandlersAnalysis/implementations/OutputProcessor.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package services.S3MHandlersAnalysis.implementations | ||
|
||
import services.S3MHandlersAnalysis.util.Utils | ||
|
||
import java.nio.file.Path | ||
import java.nio.file.Paths | ||
|
||
class OutputProcessor implements main.interfaces.OutputProcessor { | ||
|
||
private static final Path ANALYSIS_REPOSITORY_PATH = Paths.get('../merge-tools') | ||
static final String ANALYSIS_REMOTE_URL = "https://github.com/jvcoutinho/merge-tools/tree/master/s3m-handlers-analysis" | ||
|
||
@Override | ||
void processOutput() { | ||
stageAndPushData() | ||
println 'Pushed data to remote analysis repository' | ||
} | ||
|
||
private static void stageAndPushData() { | ||
// Stage changes. | ||
Utils.runGitCommand(ANALYSIS_REPOSITORY_PATH, 'add', '.') | ||
|
||
// Commit changes. | ||
Utils.runGitCommand(ANALYSIS_REPOSITORY_PATH, 'commit', '-m', 'Collected data') | ||
|
||
// Push changes. | ||
Utils.runGitCommand(ANALYSIS_REPOSITORY_PATH, 'push', '--force-with-lease') | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.