From 99da7df5c406be205b0f5ab022ebfc57179502e7 Mon Sep 17 00:00:00 2001 From: Matthias Bilger Date: Sun, 31 Oct 2021 09:28:22 +0100 Subject: [PATCH 1/6] Add Patches Reporter This reporter is available as `Patches`. It generates a patch for each mutant. --- .../generated/mull-runner-cli-options.rst | 2 + include/mull/Reporters/PatchesReporter.h | 28 +++++ include/mull/Reporters/Reporter.h | 2 +- include/mull/Reporters/SourceCodeReader.h | 1 + lib/CMakeLists.txt | 1 + lib/Reporters/PatchesReporter.cpp | 101 ++++++++++++++++++ lib/Reporters/SourceCodeReader.cpp | 10 ++ tools/CLIOptions/CLIOptions.cpp | 5 + 8 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 include/mull/Reporters/PatchesReporter.h create mode 100644 lib/Reporters/PatchesReporter.cpp diff --git a/docs/command-line/generated/mull-runner-cli-options.rst b/docs/command-line/generated/mull-runner-cli-options.rst index 00ba0688d..a3817574a 100644 --- a/docs/command-line/generated/mull-runner-cli-options.rst +++ b/docs/command-line/generated/mull-runner-cli-options.rst @@ -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 diff --git a/include/mull/Reporters/PatchesReporter.h b/include/mull/Reporters/PatchesReporter.h new file mode 100644 index 000000000..ca6aa9c43 --- /dev/null +++ b/include/mull/Reporters/PatchesReporter.h @@ -0,0 +1,28 @@ +#include "Reporter.h" + +#include "mull/Reporters/SourceCodeReader.h" +#include +#include +#include + +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 diff --git a/include/mull/Reporters/Reporter.h b/include/mull/Reporters/Reporter.h index d8b25d1cb..439bc99f2 100644 --- a/include/mull/Reporters/Reporter.h +++ b/include/mull/Reporters/Reporter.h @@ -4,7 +4,7 @@ namespace mull { class Result; -enum class ReporterKind { IDE, SQLite, Elements }; +enum class ReporterKind { IDE, SQLite, Elements, Patches }; class Reporter { public: diff --git a/include/mull/Reporters/SourceCodeReader.h b/include/mull/Reporters/SourceCodeReader.h index a45a79c96..2973956f1 100644 --- a/include/mull/Reporters/SourceCodeReader.h +++ b/include/mull/Reporters/SourceCodeReader.h @@ -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; }; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 1a8ee2d3a..d0aea210d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -39,6 +39,7 @@ set(mull_sources Reporters/SourceCodeReader.cpp Reporters/SourceManager.cpp Reporters/SQLiteReporter.cpp + Reporters/PatchesReporter.cpp SourceLocation.cpp diff --git a/lib/Reporters/PatchesReporter.cpp b/lib/Reporters/PatchesReporter.cpp new file mode 100644 index 000000000..15cfc8715 --- /dev/null +++ b/lib/Reporters/PatchesReporter.cpp @@ -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 +#include +#include +#include +#include + +#include +#include +#include +#include + +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 + "'"); +} diff --git a/lib/Reporters/SourceCodeReader.cpp b/lib/Reporters/SourceCodeReader.cpp index 84eae6d59..ea913e579 100644 --- a/lib/Reporters/SourceCodeReader.cpp +++ b/lib/Reporters/SourceCodeReader.cpp @@ -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(); +} diff --git a/tools/CLIOptions/CLIOptions.cpp b/tools/CLIOptions/CLIOptions.cpp index 8883323da..a2bbbc18f 100644 --- a/tools/CLIOptions/CLIOptions.cpp +++ b/tools/CLIOptions/CLIOptions.cpp @@ -4,6 +4,7 @@ #include #include #include +#include using namespace mull; using namespace tool; @@ -44,6 +45,7 @@ static std::vector reporterOptions({ { "Elements", "Generates mutation-testing-elements compatible JSON file", ReporterKind::Elements }, + { "Patches", "Saves results into Patchfiles", ReporterKind::Patches }, }); ReportersCLIOptions::ReportersCLIOptions(Diagnostics &diagnostics, list ¶meter) @@ -67,6 +69,9 @@ std::vector> 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 " From 1fffc90d659a49ff1ff1cf6e4d25ce8cca6d0aba Mon Sep 17 00:00:00 2001 From: Matthias Bilger Date: Sun, 31 Oct 2021 23:42:32 +0100 Subject: [PATCH 2/6] Add git-project-root as base dir for patch --- include/mull/Reporters/PatchesReporter.h | 4 ++- lib/Reporters/PatchesReporter.cpp | 34 ++++++++++++++++-------- tools/CLIOptions/CLIOptions.cpp | 2 +- tools/CLIOptions/CLIOptions.h | 1 + tools/mull-cxx/mull-cxx.cpp | 1 + 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/include/mull/Reporters/PatchesReporter.h b/include/mull/Reporters/PatchesReporter.h index ca6aa9c43..424540586 100644 --- a/include/mull/Reporters/PatchesReporter.h +++ b/include/mull/Reporters/PatchesReporter.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace mull { @@ -13,7 +14,7 @@ class Diagnostics; class PatchesReporter : public Reporter { public: explicit PatchesReporter(Diagnostics &diagnostics, const std::string &reportDir = "", - const std::string &reportName = ""); + const std::string &reportName = "", const std::string basePath = ""); void reportResults(const Result &result) override; @@ -22,6 +23,7 @@ class PatchesReporter : public Reporter { private: Diagnostics &diagnostics; std::string patchesPath; + std::regex basePathRegex; SourceCodeReader sourceCodeReader; }; diff --git a/lib/Reporters/PatchesReporter.cpp b/lib/Reporters/PatchesReporter.cpp index 15cfc8715..f51d51959 100644 --- a/lib/Reporters/PatchesReporter.cpp +++ b/lib/Reporters/PatchesReporter.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include using namespace mull; @@ -43,9 +44,10 @@ static std::string getReportDir(const std::string &reportDir) { } PatchesReporter::PatchesReporter(Diagnostics &diagnostics, const std::string &reportDir, - const std::string &reportName) + const std::string &reportName, const std::string basePath) : diagnostics(diagnostics), patchesPath(getReportDir(reportDir) + "/" + getReportName(reportName)) , + basePathRegex("^" + getReportDir(basePath)), sourceCodeReader() { llvm::sys::fs::create_directories(patchesPath, true); } @@ -61,13 +63,14 @@ void mull::PatchesReporter::reportResults(const Result &result) { const ExecutionResult mutationExecutionResult = mutationResult->getExecutionResult(); - std::stringstream filename; + std::stringstream filenamebuilder; auto mutant = *mutationResult->getMutant(); auto& sourceLocation = mutant.getSourceLocation(); auto& sourceEndLocation = mutant.getEndLocation(); - std::string sourceBasename = sourceLocation.filePath.substr(sourceLocation.directory.size()+1); + const std::string sourceBasename = std::regex_replace(sourceLocation.filePath.substr(sourceLocation.directory.size()+1), std::regex("([/]|\\.(?!patch))"), "_");; auto mutator = factory.getMutator(mutant.getMutatorIdentifier()); - std::string sourceLine = sourceCodeReader.getSourceLine(sourceLocation); + const std::string sourceLine = sourceCodeReader.getSourceLine(sourceLocation); + const std::string sourcePath = std::regex_replace(sourceLocation.filePath, basePathRegex, ""); auto prefix = [&mutationExecutionResult](){ switch(mutationExecutionResult.status){ @@ -81,21 +84,30 @@ void mull::PatchesReporter::reportResults(const Result &result) { return "killed-"; } }; - - filename << patchesPath << "/" << prefix() << "-" - << sourceBasename << mutant.getMutatorIdentifier() + filenamebuilder << 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" + const std::string filename = filenamebuilder.str(); + + std::ofstream myfile{filename}; + if(!myfile.good()) + diagnostics.warning(std::string("Writing File failed") + filename.c_str()); + myfile << "--- a" << sourcePath << " 0" << "\n" + << "+++ b" << sourcePath << " 0" << "\n" << "@@ -" << sourceLocation.line << ",1 +" << sourceLocation.line << ",1 @@\n" << "-" << sourceLine << "+" << sourceLine.substr(0, sourceLocation.column-1) << mutator->getReplacement() << sourceLine.substr(sourceEndLocation.column-1) ; + if(!myfile.good()) + diagnostics.warning(std::string("Writing File failed")); + myfile.flush(); + if(!myfile.good()) + diagnostics.warning(std::string("Writing File failed") + filename.c_str()); myfile.close(); + diagnostics.debug(std::string("Writing Patchfile: ") + filename.c_str()); } - diagnostics.info(std::string("Patchefiles can be found at '") + patchesPath + "'"); + diagnostics.info(std::string("Patchfiles can be found at '") + patchesPath + "'"); } diff --git a/tools/CLIOptions/CLIOptions.cpp b/tools/CLIOptions/CLIOptions.cpp index a2bbbc18f..2a70d691a 100644 --- a/tools/CLIOptions/CLIOptions.cpp +++ b/tools/CLIOptions/CLIOptions.cpp @@ -70,7 +70,7 @@ std::vector> ReportersCLIOptions::reporters(ReporterPa reporters.emplace_back(new mull::SQLiteReporter(diagnostics, directory, name)); } break; case ReporterKind::Patches: { - reporters.emplace_back(new mull::PatchesReporter(diagnostics, directory, name)); + reporters.emplace_back(new mull::PatchesReporter(diagnostics, directory, name, params.gitDir)); } break; case ReporterKind::Elements: { if (!params.compilationDatabaseAvailable) { diff --git a/tools/CLIOptions/CLIOptions.h b/tools/CLIOptions/CLIOptions.h index 4777816a6..aee6bf48e 100644 --- a/tools/CLIOptions/CLIOptions.h +++ b/tools/CLIOptions/CLIOptions.h @@ -311,6 +311,7 @@ class MutatorsCLIOptions { struct ReporterParameters { std::string reporterName; std::string reporterDirectory; + std::string gitDir; bool compilationDatabaseAvailable; bool IDEReporterShowKilled; }; diff --git a/tools/mull-cxx/mull-cxx.cpp b/tools/mull-cxx/mull-cxx.cpp index e077b65b0..fddde145e 100644 --- a/tools/mull-cxx/mull-cxx.cpp +++ b/tools/mull-cxx/mull-cxx.cpp @@ -190,6 +190,7 @@ int main(int argc, char **argv) { tool::ReporterParameters params{ .reporterName = tool::ReportName.getValue(), .reporterDirectory = tool::ReportDirectory.getValue(), .compilationDatabaseAvailable = compilationDatabaseInfoAvailable, + .gitDir = tool::GitProjectRoot.getValue(), .IDEReporterShowKilled = tool::IDEReporterShowKilled }; std::vector> reporters = reportersOption.reporters(params); From dfae5a0d3d5a2fae7108b6f8134a9275afe521ce Mon Sep 17 00:00:00 2001 From: Matthias Bilger Date: Sun, 31 Oct 2021 23:43:10 +0100 Subject: [PATCH 3/6] Add patch-reporter lit tests --- .../tests/patch-reporter/equality/main.cpp | 28 ++++++++++++++ .../patch-reporter/git_dir_relative/main.cpp | 28 ++++++++++++++ .../patch-reporter/remove_void_call/main.cpp | 37 +++++++++++++++++++ tests-lit/tests/patch-reporter/shift/main.cpp | 28 ++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 tests-lit/tests/patch-reporter/equality/main.cpp create mode 100644 tests-lit/tests/patch-reporter/git_dir_relative/main.cpp create mode 100644 tests-lit/tests/patch-reporter/remove_void_call/main.cpp create mode 100644 tests-lit/tests/patch-reporter/shift/main.cpp diff --git a/tests-lit/tests/patch-reporter/equality/main.cpp b/tests-lit/tests/patch-reporter/equality/main.cpp new file mode 100644 index 000000000..811ac4b67 --- /dev/null +++ b/tests-lit/tests/patch-reporter/equality/main.cpp @@ -0,0 +1,28 @@ +int equal(int a, int b) { + return a == b; +} + +int main() { + return equal(2, 2) != 1; +} + +// clang-format off +/** + +RUN: cd %S +RUN: mkdir -p %S/Output/sandbox +RUN: cp %S/main.cpp %S/Output/sandbox/main.cpp +RUN: cd %S/Output/sandbox + +/// We cd to the the test directory and compile using relative paths. +RUN: cd %S; %clang_cxx %sysroot -fembed-bitcode -g -O0 Output/sandbox/main.cpp -o Output/main.cpp.exe + +RUN: cd %S/Output && echo $PATH; (unset TERM; %mull_cxx -mutators=cxx_eq_to_ne -linker-flags="%sysroot" --linker=%clang_cxx -debug main.cpp.exe --report-name test --reporters Patches --reporters IDE; test $? = 0; ls -R %S/Output/test-patches; cat %S/Output/test-patches/killed-Output_sandbox_main_cpp-cxx_eq_to_ne-L2-C12.patch) | %filecheck %s --dump-input=fail --strict-whitespace --match-full-lines + +CHECK:[debug] Writing Patchfile: {{.*}} +CHECK:[info] Patchfiles can be found at './test-patches' +CHECK:{{.*}}main_cpp{{.*}} +CHECK:--- a{{.*}}/Output/sandbox/main.cpp 0 +CHECK:+{{\s+}}return a != b; + +*/ diff --git a/tests-lit/tests/patch-reporter/git_dir_relative/main.cpp b/tests-lit/tests/patch-reporter/git_dir_relative/main.cpp new file mode 100644 index 000000000..b11e695e2 --- /dev/null +++ b/tests-lit/tests/patch-reporter/git_dir_relative/main.cpp @@ -0,0 +1,28 @@ +int equal(int a, int b) { + return a == b; +} + +int main() { + return equal(2, 2) != 1; +} + +// clang-format off +/** + +RUN: cd %S +RUN: mkdir -p %S/Output/sandbox +RUN: cp %S/main.cpp %S/Output/sandbox/main.cpp +RUN: cd %S/Output/sandbox + +/// We cd to the the test directory and compile using relative paths. +RUN: cd %S; %clang_cxx %sysroot -fembed-bitcode -g -O0 Output/sandbox/main.cpp -o Output/main.cpp.exe + +RUN: cd %S/Output && echo $PATH; (unset TERM; %mull_cxx -mutators=cxx_eq_to_ne -linker-flags="%sysroot" --git-project-root %S --linker=%clang_cxx -debug main.cpp.exe --report-name test --reporters Patches --reporters IDE; test $? = 0; ls -R %S/Output/test-patches; cat %S/Output/test-patches/killed-Output_sandbox_main_cpp-cxx_eq_to_ne-L2-C12.patch) | %filecheck %s --dump-input=fail --strict-whitespace --match-full-lines + +CHECK:[debug] Writing Patchfile: {{.*}} +CHECK:[info] Patchfiles can be found at './test-patches' +CHECK:{{.*}}main_cpp{{.*}} +CHECK:--- a/Output/sandbox/main.cpp 0 +CHECK:+{{\s+}}return a != b; + +*/ diff --git a/tests-lit/tests/patch-reporter/remove_void_call/main.cpp b/tests-lit/tests/patch-reporter/remove_void_call/main.cpp new file mode 100644 index 000000000..533108d28 --- /dev/null +++ b/tests-lit/tests/patch-reporter/remove_void_call/main.cpp @@ -0,0 +1,37 @@ +static int globalVar = -1; +void voidFunction() { + globalVar = 0; +} + +int foo() { + voidFunction(); + return 0; +}; + +int main() { + foo(); + return globalVar; +} + +// clang-format off +/** + +RUN: cd %S +RUN: mkdir -p %S/Output/sandbox +RUN: cp %S/main.cpp %S/Output/sandbox/main.cpp +RUN: cd %S/Output/sandbox + +/// We cd to the the test directory and compile using relative paths. +RUN: cd %S; %clang_cxx %sysroot -fembed-bitcode -g -O0 Output/sandbox/main.cpp -o Output/main.cpp.exe + +RUN: cd %S/Output && echo $PATH; (unset TERM; %mull_cxx -mutators=cxx_calls -linker-flags="%sysroot" --linker=%clang_cxx -debug main.cpp.exe --report-name test --reporters Patches --reporters IDE; test $? = 0; ls -R %S/Output/test-patches; cat %S/Output/test-patches/killed-Output_sandbox_main_cpp-cxx_remove_void_call-L7-C3.patch; cat %S/Output/test-patches/killed-Output_sandbox_main_cpp-cxx_replace_scalar_call-L12-C3.patch) | %filecheck %s --dump-input=fail --strict-whitespace --match-full-lines + +CHECK:[debug] Writing Patchfile: {{.*}} +CHECK:[info] Patchfiles can be found at './test-patches' +CHECK:{{.*}}main_cpp-cxx_remove_void{{.*}} +CHECK:{{.*}}main_cpp-cxx_replace_scalar{{.*}} +CHECK:--- a{{.*}}/Output/sandbox/main.cpp 0 +CHECK:+{{\s+}}; +CHECK:+{{\s+}}42; + +*/ diff --git a/tests-lit/tests/patch-reporter/shift/main.cpp b/tests-lit/tests/patch-reporter/shift/main.cpp new file mode 100644 index 000000000..677a2aa59 --- /dev/null +++ b/tests-lit/tests/patch-reporter/shift/main.cpp @@ -0,0 +1,28 @@ +int bitwise_left_shift(int a, int b) { + return a << b; +} + +int main() { + return ! (bitwise_left_shift(1, 2) == 4); +} + +// clang-format off +/** + +RUN: cd %S +RUN: mkdir -p %S/Output/sandbox +RUN: cp %S/main.cpp %S/Output/sandbox/main.cpp +RUN: cd %S/Output/sandbox + +/// We cd to the the test directory and compile using relative paths. +RUN: cd %S; %clang_cxx %sysroot -fembed-bitcode -g -O0 Output/sandbox/main.cpp -o Output/main.cpp.exe + +RUN: cd %S/Output && echo $PATH; (unset TERM; %mull_cxx -mutators=cxx_bitwise -linker-flags="%sysroot" --linker=%clang_cxx -debug main.cpp.exe --report-name test --reporters Patches --reporters IDE; test $? = 0; ls -R %S/Output/test-patches; cat %S/Output/test-patches/killed-Output_sandbox_main_cpp-cxx_lshift_to_rshift-L2-C12.patch) | %filecheck %s --dump-input=fail --strict-whitespace --match-full-lines + +CHECK:[debug] Writing Patchfile: {{.*}} +CHECK:[info] Patchfiles can be found at './test-patches' +CHECK:killed-{{.*}}main_cpp{{.*}} +CHECK:--- a{{.*}}/Output/sandbox/main.cpp 0 +CHECK:+{{\s+}}return a >> b; + +*/ From f7aa8f8b4a22b9298126f88ea8e2e63e5d7add29 Mon Sep 17 00:00:00 2001 From: Matthias Bilger Date: Sun, 31 Oct 2021 23:46:24 +0100 Subject: [PATCH 4/6] Add documentation for Patch Reporter --- docs/command-line/generated/mull-cxx-cli-options.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/command-line/generated/mull-cxx-cli-options.rst b/docs/command-line/generated/mull-cxx-cli-options.rst index 6052e157f..a57169a9d 100644 --- a/docs/command-line/generated/mull-cxx-cli-options.rst +++ b/docs/command-line/generated/mull-cxx-cli-options.rst @@ -20,6 +20,8 @@ :Elements: Generates mutation-testing-elements compatible JSON file + :Patches: Generates a patchfile for each mutation. uses `--git-project-root` as base dir. + --ide-reporter-show-killed Makes IDEReporter to also report killed mutations (disabled by default) --debug Enables Debug Mode: more logs are printed From de83b76a3dd8bb17febc1b0ce31ab42dc562e746 Mon Sep 17 00:00:00 2001 From: Matthias Bilger Date: Mon, 1 Nov 2021 00:11:33 +0100 Subject: [PATCH 5/6] use extra parameter for patch base path --- .../reporter_path_base/main.cpp | 28 +++++++++++++++++++ tools/CLIOptions/CLIOptions.cpp | 2 +- tools/CLIOptions/CLIOptions.h | 11 +++++++- tools/mull-cxx/mull-cxx-cli.h | 2 ++ tools/mull-cxx/mull-cxx.cpp | 5 +++- tools/mull-runner/mull-runner-cli.h | 1 + tools/mull-runner/mull-runner.cpp | 3 ++ 7 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 tests-lit/tests/patch-reporter/reporter_path_base/main.cpp diff --git a/tests-lit/tests/patch-reporter/reporter_path_base/main.cpp b/tests-lit/tests/patch-reporter/reporter_path_base/main.cpp new file mode 100644 index 000000000..2faf5341d --- /dev/null +++ b/tests-lit/tests/patch-reporter/reporter_path_base/main.cpp @@ -0,0 +1,28 @@ +int equal(int a, int b) { + return a == b; +} + +int main() { + return equal(2, 2) != 1; +} + +// clang-format off +/** + +RUN: cd %S +RUN: mkdir -p %S/Output/sandbox +RUN: cp %S/main.cpp %S/Output/sandbox/main.cpp +RUN: cd %S/Output/sandbox + +/// We cd to the the test directory and compile using relative paths. +RUN: cd %S; %clang_cxx %sysroot -fembed-bitcode -g -O0 Output/sandbox/main.cpp -o Output/main.cpp.exe + +RUN: cd %S/Output && echo $PATH; (unset TERM; %mull_cxx -mutators=cxx_eq_to_ne -linker-flags="%sysroot" --git-project-root %S/Output --report-patch-base %S --linker=%clang_cxx -debug main.cpp.exe --report-name test --reporters Patches --reporters IDE; test $? = 0; ls -R %S/Output/test-patches; cat %S/Output/test-patches/killed-Output_sandbox_main_cpp-cxx_eq_to_ne-L2-C12.patch) | %filecheck %s --dump-input=fail --strict-whitespace --match-full-lines + +CHECK:[debug] Writing Patchfile: {{.*}} +CHECK:[info] Patchfiles can be found at './test-patches' +CHECK:{{.*}}main_cpp{{.*}} +CHECK:--- a/Output/sandbox/main.cpp 0 +CHECK:+{{\s+}}return a != b; + +*/ diff --git a/tools/CLIOptions/CLIOptions.cpp b/tools/CLIOptions/CLIOptions.cpp index 2a70d691a..b83b1dcf3 100644 --- a/tools/CLIOptions/CLIOptions.cpp +++ b/tools/CLIOptions/CLIOptions.cpp @@ -70,7 +70,7 @@ std::vector> ReportersCLIOptions::reporters(ReporterPa reporters.emplace_back(new mull::SQLiteReporter(diagnostics, directory, name)); } break; case ReporterKind::Patches: { - reporters.emplace_back(new mull::PatchesReporter(diagnostics, directory, name, params.gitDir)); + reporters.emplace_back(new mull::PatchesReporter(diagnostics, directory, name, params.patchBasePathDir)); } break; case ReporterKind::Elements: { if (!params.compilationDatabaseAvailable) { diff --git a/tools/CLIOptions/CLIOptions.h b/tools/CLIOptions/CLIOptions.h index aee6bf48e..e8516f556 100644 --- a/tools/CLIOptions/CLIOptions.h +++ b/tools/CLIOptions/CLIOptions.h @@ -79,6 +79,15 @@ opt ReportName( \ init(""), \ cat(MullCategory)) +#define ReportPatchBaseDirectory_() \ +opt ReportPatchBaseDirectory( \ + "report-patch-base", \ + desc("Create Patches relative to this directory (defaults to git-project-root if available, else absolute path will be used)"), \ + Optional, \ + value_desc("directory"), \ + init("."), \ + cat(MullCategory)) + #define DisableJunkDetection_() \ opt DisableJunkDetection( \ "disable-junk-detection", \ @@ -311,7 +320,7 @@ class MutatorsCLIOptions { struct ReporterParameters { std::string reporterName; std::string reporterDirectory; - std::string gitDir; + std::string patchBasePathDir; bool compilationDatabaseAvailable; bool IDEReporterShowKilled; }; diff --git a/tools/mull-cxx/mull-cxx-cli.h b/tools/mull-cxx/mull-cxx-cli.h index 5b4bde3c8..d378447a5 100644 --- a/tools/mull-cxx/mull-cxx-cli.h +++ b/tools/mull-cxx/mull-cxx-cli.h @@ -21,6 +21,7 @@ NoTestOutput_(); NoMutantOutput_(); ReportName_(); ReportDirectory_(); +ReportPatchBaseDirectory_(); Mutators_(); DryRunOption_(); Linker_(); @@ -54,6 +55,7 @@ void dumpCLIInterface(Diagnostics &diagnostics) { &ReportName, &ReportDirectory, + &ReportPatchBaseDirectory, reporters, &IDEReporterShowKilled, &DebugEnabled, diff --git a/tools/mull-cxx/mull-cxx.cpp b/tools/mull-cxx/mull-cxx.cpp index fddde145e..5d8133ad8 100644 --- a/tools/mull-cxx/mull-cxx.cpp +++ b/tools/mull-cxx/mull-cxx.cpp @@ -189,9 +189,12 @@ int main(int argc, char **argv) { tool::ReporterParameters params{ .reporterName = tool::ReportName.getValue(), .reporterDirectory = tool::ReportDirectory.getValue(), + .patchBasePathDir = tool::ReportPatchBaseDirectory.getValue(), .compilationDatabaseAvailable = compilationDatabaseInfoAvailable, - .gitDir = tool::GitProjectRoot.getValue(), .IDEReporterShowKilled = tool::IDEReporterShowKilled }; + if(tool::ReportPatchBaseDirectory.getValue() == "." && tool::GitProjectRoot.getValue() != "."){ + params.patchBasePathDir = tool::GitProjectRoot.getValue(); + } std::vector> reporters = reportersOption.reporters(params); mull::CXXJunkDetector junkDetector(diagnostics, astStorage); diff --git a/tools/mull-runner/mull-runner-cli.h b/tools/mull-runner/mull-runner-cli.h index a4168745c..eb2a4e3dd 100644 --- a/tools/mull-runner/mull-runner-cli.h +++ b/tools/mull-runner/mull-runner-cli.h @@ -19,6 +19,7 @@ NoTestOutput_(); NoMutantOutput_(); ReportName_(); ReportDirectory_(); +ReportPatchBaseDirectory_(); IDEReporterShowKilled_(); IncludeNotCovered_(); RunnerArgs_(); diff --git a/tools/mull-runner/mull-runner.cpp b/tools/mull-runner/mull-runner.cpp index 2cc2ef247..5f96ba2f2 100644 --- a/tools/mull-runner/mull-runner.cpp +++ b/tools/mull-runner/mull-runner.cpp @@ -82,10 +82,13 @@ int main(int argc, char **argv) { configuration.captureMutantOutput = false; } + tool::ReporterParameters params{ .reporterName = tool::ReportName.getValue(), .reporterDirectory = tool::ReportDirectory.getValue(), + .patchBasePathDir = tool::ReportPatchBaseDirectory.getValue(), .compilationDatabaseAvailable = false, .IDEReporterShowKilled = tool::IDEReporterShowKilled }; + std::vector> reporters = reportersOption.reporters(params); std::string executable = inputFile; From 4c05baf60f57e4f57ea9c97f243f07c336111c6f Mon Sep 17 00:00:00 2001 From: Matthias Bilger Date: Mon, 1 Nov 2021 06:09:45 +0100 Subject: [PATCH 6/6] Improve PatchReporter code --- lib/Reporters/PatchesReporter.cpp | 34 ++++++++++++++--------------- lib/Reporters/SourceCodeReader.cpp | 6 +---- tools/CLIOptions/CLIOptions.cpp | 2 +- tools/mull-runner/mull-runner-cli.h | 1 + tools/mull-runner/mull-runner.cpp | 2 -- 5 files changed, 19 insertions(+), 26 deletions(-) diff --git a/lib/Reporters/PatchesReporter.cpp b/lib/Reporters/PatchesReporter.cpp index f51d51959..537180a43 100644 --- a/lib/Reporters/PatchesReporter.cpp +++ b/lib/Reporters/PatchesReporter.cpp @@ -63,16 +63,15 @@ void mull::PatchesReporter::reportResults(const Result &result) { const ExecutionResult mutationExecutionResult = mutationResult->getExecutionResult(); - std::stringstream filenamebuilder; - auto mutant = *mutationResult->getMutant(); - auto& sourceLocation = mutant.getSourceLocation(); - auto& sourceEndLocation = mutant.getEndLocation(); + const auto mutant = *mutationResult->getMutant(); + const auto& sourceLocation = mutant.getSourceLocation(); + const auto& sourceEndLocation = mutant.getEndLocation(); const std::string sourceBasename = std::regex_replace(sourceLocation.filePath.substr(sourceLocation.directory.size()+1), std::regex("([/]|\\.(?!patch))"), "_");; - auto mutator = factory.getMutator(mutant.getMutatorIdentifier()); + const auto mutator = factory.getMutator(mutant.getMutatorIdentifier()); const std::string sourceLine = sourceCodeReader.getSourceLine(sourceLocation); const std::string sourcePath = std::regex_replace(sourceLocation.filePath, basePathRegex, ""); - auto prefix = [&mutationExecutionResult](){ + const std::string prefix = [&mutationExecutionResult](){ switch(mutationExecutionResult.status){ case ExecutionStatus::Passed: return "survived-"; @@ -83,30 +82,29 @@ void mull::PatchesReporter::reportResults(const Result &result) { default: return "killed-"; } - }; - filenamebuilder << patchesPath << "/" << prefix() - << sourceBasename << "-" << mutant.getMutatorIdentifier() - << "-L" << sourceLocation.line << "-C" << sourceLocation.column - << ".patch"; + }(); - const std::string filename = filenamebuilder.str(); + const std::string filename =[&](){ + std::stringstream filenamebuilder; + filenamebuilder << patchesPath << "/" << prefix + << sourceBasename << "-" << mutant.getMutatorIdentifier() + << "-L" << sourceLocation.line << "-C" << sourceLocation.column + << ".patch"; + return filenamebuilder.str(); + }(); + diagnostics.debug(std::string("Writing Patchfile: ") + filename.c_str()); std::ofstream myfile{filename}; - if(!myfile.good()) - diagnostics.warning(std::string("Writing File failed") + filename.c_str()); myfile << "--- a" << sourcePath << " 0" << "\n" << "+++ b" << sourcePath << " 0" << "\n" << "@@ -" << sourceLocation.line << ",1 +" << sourceLocation.line << ",1 @@\n" << "-" << sourceLine << "+" << sourceLine.substr(0, sourceLocation.column-1) << mutator->getReplacement() << sourceLine.substr(sourceEndLocation.column-1) ; - if(!myfile.good()) - diagnostics.warning(std::string("Writing File failed")); myfile.flush(); if(!myfile.good()) - diagnostics.warning(std::string("Writing File failed") + filename.c_str()); + diagnostics.warning(std::string("Writing Patchfile failed") + filename.c_str()); myfile.close(); - diagnostics.debug(std::string("Writing Patchfile: ") + filename.c_str()); } diagnostics.info(std::string("Patchfiles can be found at '") + patchesPath + "'"); diff --git a/lib/Reporters/SourceCodeReader.cpp b/lib/Reporters/SourceCodeReader.cpp index ea913e579..dd70293c0 100644 --- a/lib/Reporters/SourceCodeReader.cpp +++ b/lib/Reporters/SourceCodeReader.cpp @@ -93,11 +93,7 @@ std::string SourceCodeReader::getSourceLineWithCaret(const SourceLocation &sourc } 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(); + return line; } diff --git a/tools/CLIOptions/CLIOptions.cpp b/tools/CLIOptions/CLIOptions.cpp index b83b1dcf3..1306cc261 100644 --- a/tools/CLIOptions/CLIOptions.cpp +++ b/tools/CLIOptions/CLIOptions.cpp @@ -45,7 +45,7 @@ static std::vector reporterOptions({ { "Elements", "Generates mutation-testing-elements compatible JSON file", ReporterKind::Elements }, - { "Patches", "Saves results into Patchfiles", ReporterKind::Patches }, + { "Patches", "Generates patch file for each mutation", ReporterKind::Patches }, }); ReportersCLIOptions::ReportersCLIOptions(Diagnostics &diagnostics, list ¶meter) diff --git a/tools/mull-runner/mull-runner-cli.h b/tools/mull-runner/mull-runner-cli.h index eb2a4e3dd..724cfec32 100644 --- a/tools/mull-runner/mull-runner-cli.h +++ b/tools/mull-runner/mull-runner-cli.h @@ -36,6 +36,7 @@ void dumpCLIInterface(mull::Diagnostics &diagnostics) { &ReportName, &ReportDirectory, + &ReportPatchBaseDirectory, reporters, &IDEReporterShowKilled, &DebugEnabled, diff --git a/tools/mull-runner/mull-runner.cpp b/tools/mull-runner/mull-runner.cpp index 5f96ba2f2..fa9ba488f 100644 --- a/tools/mull-runner/mull-runner.cpp +++ b/tools/mull-runner/mull-runner.cpp @@ -82,13 +82,11 @@ int main(int argc, char **argv) { configuration.captureMutantOutput = false; } - tool::ReporterParameters params{ .reporterName = tool::ReportName.getValue(), .reporterDirectory = tool::ReportDirectory.getValue(), .patchBasePathDir = tool::ReportPatchBaseDirectory.getValue(), .compilationDatabaseAvailable = false, .IDEReporterShowKilled = tool::IDEReporterShowKilled }; - std::vector> reporters = reportersOption.reporters(params); std::string executable = inputFile;