Skip to content

Commit

Permalink
Add Patches Reporter
Browse files Browse the repository at this point in the history
This reporter is available as `Patches`. It generates a patch for each mutant.
  • Loading branch information
m42e committed Oct 31, 2021
1 parent ccb039d commit 03da7e8
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 1 deletion.
2 changes: 2 additions & 0 deletions docs/command-line/generated/mull-runner-cli-options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

:Elements: Generates mutation-testing-elements compatible JSON file

:Patches: Generates a unified patchfile for each mutation

--ide-reporter-show-killed Makes IDEReporter to also report killed mutations (disabled by default)

--debug Enables Debug Mode: more logs are printed
Expand Down
28 changes: 28 additions & 0 deletions include/mull/Reporters/PatchesReporter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "Reporter.h"

#include "mull/Reporters/SourceCodeReader.h"
#include <memory>
#include <string>
#include <vector>

namespace mull {

class Result;
class Diagnostics;

class PatchesReporter : public Reporter {
public:
explicit PatchesReporter(Diagnostics &diagnostics, const std::string &reportDir = "",
const std::string &reportName = "");

void reportResults(const Result &result) override;

std::string getPatchesPath();

private:
Diagnostics &diagnostics;
std::string patchesPath;
SourceCodeReader sourceCodeReader;
};

} // namespace mull
2 changes: 1 addition & 1 deletion include/mull/Reporters/Reporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace mull {

class Result;

enum class ReporterKind { IDE, SQLite, Elements };
enum class ReporterKind { IDE, SQLite, Elements, Patches };

class Reporter {
public:
Expand Down
1 change: 1 addition & 0 deletions include/mull/Reporters/SourceCodeReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class SourceCodeReader {
SourceCodeReader();
std::string getContext(const mull::SourceLocation &sourceLocation);
std::string getSourceLineWithCaret(const SourceLocation &sourceLocation);
std::string getSourceLine(const SourceLocation &sourceLocation);
private:
SourceManager sourceManager;
};
Expand Down
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ set(mull_sources
Reporters/SourceCodeReader.cpp
Reporters/SourceManager.cpp
Reporters/SQLiteReporter.cpp
Reporters/PatchesReporter.cpp

SourceLocation.cpp

Expand Down
101 changes: 101 additions & 0 deletions lib/Reporters/PatchesReporter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include "mull/Reporters/PatchesReporter.h"

#include "mull/Bitcode.h"
#include "mull/Diagnostics/Diagnostics.h"
#include "mull/ExecutionResult.h"
#include "mull/Mutant.h"
#include "mull/Mutators/Mutator.h"
#include "mull/Mutators/MutatorsFactory.h"
#include "mull/Result.h"
#include "mull/SourceLocation.h"
#include "mull/Reporters/SourceCodeReader.h"

#include <llvm/IR/DebugInfoMetadata.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Instruction.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/FileSystem.h>

#include <fstream>
#include <sstream>
#include <string>
#include <unistd.h>

using namespace mull;
using namespace llvm;


static std::string getReportName(const std::string &name) {
std::string reportName = name;
if (reportName.empty()) {
time_t t;
time(&t);
reportName = std::to_string(t);
}
return reportName + "-patches";
}

static std::string getReportDir(const std::string &reportDir) {
if (reportDir.empty()) {
return std::string(".");
}
return reportDir;
}

PatchesReporter::PatchesReporter(Diagnostics &diagnostics, const std::string &reportDir,
const std::string &reportName)
: diagnostics(diagnostics),
patchesPath(getReportDir(reportDir) + "/" + getReportName(reportName)) ,
sourceCodeReader() {
llvm::sys::fs::create_directories(patchesPath, true);
}

std::string mull::PatchesReporter::getPatchesPath() {
return patchesPath;
}

void mull::PatchesReporter::reportResults(const Result &result) {
MutatorsFactory factory(diagnostics);
factory.init();
for (auto &mutationResult : result.getMutationResults()) {

const ExecutionResult mutationExecutionResult = mutationResult->getExecutionResult();

std::stringstream filename;
auto mutant = *mutationResult->getMutant();
auto& sourceLocation = mutant.getSourceLocation();
auto& sourceEndLocation = mutant.getEndLocation();
std::string sourceBasename = sourceLocation.filePath.substr(sourceLocation.directory.size()+1);
auto mutator = factory.getMutator(mutant.getMutatorIdentifier());
std::string sourceLine = sourceCodeReader.getSourceLine(sourceLocation);

auto prefix = [&mutationExecutionResult](){
switch(mutationExecutionResult.status){
case ExecutionStatus::Passed:
return "survived-";
break;
case ExecutionStatus::NotCovered:
return "uncovered-";
break;
default:
return "killed-";
}
};

filename << patchesPath << "/" << prefix() << "-"
<< sourceBasename << mutant.getMutatorIdentifier()
<< "-L" << sourceLocation.line << "-C" << sourceLocation.column
<< ".patch";

std::ofstream myfile{filename.str()};
myfile << "--- a" << sourceLocation.filePath << " 0" << "\n"
<< "+++ b" << sourceLocation.filePath << " 0" << "\n"
<< "@@ -" << sourceLocation.line << ",1 +" << sourceLocation.line << ",1 @@\n"
<< "-" << sourceLine
<< "+" << sourceLine.substr(0, sourceLocation.column-1)
<< mutator->getReplacement() << sourceLine.substr(sourceEndLocation.column-1) ;
myfile.close();
}

diagnostics.info(std::string("Patchefiles can be found at '") + patchesPath + "'");
}
10 changes: 10 additions & 0 deletions lib/Reporters/SourceCodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,13 @@ std::string SourceCodeReader::getSourceLineWithCaret(const SourceLocation &sourc
ss << line << caret << "\n";
return ss.str();
}

std::string SourceCodeReader::getSourceLine(const SourceLocation &sourceLocation) {
std::stringstream ss;

auto line = sourceManager.getLine(sourceLocation);
assert(sourceLocation.column < line.size());

ss << line;
return ss.str();
}
5 changes: 5 additions & 0 deletions tools/CLIOptions/CLIOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <mull/Reporters/IDEReporter.h>
#include <mull/Reporters/MutationTestingElementsReporter.h>
#include <mull/Reporters/SQLiteReporter.h>
#include <mull/Reporters/PatchesReporter.h>

using namespace mull;
using namespace tool;
Expand Down Expand Up @@ -44,6 +45,7 @@ static std::vector<ReporterDefinition> reporterOptions({
{ "Elements",
"Generates mutation-testing-elements compatible JSON file",
ReporterKind::Elements },
{ "Patches", "Saves results into Patchfiles", ReporterKind::Patches },
});

ReportersCLIOptions::ReportersCLIOptions(Diagnostics &diagnostics, list<ReporterKind> &parameter)
Expand All @@ -67,6 +69,9 @@ std::vector<std::unique_ptr<Reporter>> ReportersCLIOptions::reporters(ReporterPa
case ReporterKind::SQLite: {
reporters.emplace_back(new mull::SQLiteReporter(diagnostics, directory, name));
} break;
case ReporterKind::Patches: {
reporters.emplace_back(new mull::PatchesReporter(diagnostics, directory, name));
} break;
case ReporterKind::Elements: {
if (!params.compilationDatabaseAvailable) {
diagnostics.warning("Mutation Testing Elements Reporter may not work without compilation "
Expand Down

0 comments on commit 03da7e8

Please sign in to comment.