// Approval Tests version v.4.0.0 // More information at: https://github.com/approvals/ApprovalTests.cpp #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ******************** From: Blocker.h #ifndef APPROVALTESTS_CPP_BLOCKER_H #define APPROVALTESTS_CPP_BLOCKER_H class Blocker { public: virtual ~Blocker() = default; virtual bool isBlockingOnThisMachine() const = 0; }; #endif // ******************** From: Macros.h #ifndef APPROVALTESTS_CPP_MACROS_H #define APPROVALTESTS_CPP_MACROS_H #define APPROVAL_TESTS_MACROS_STATIC(type, name, defaultValue) \ static type &name(type *value = NULL) { \ static type *staticValue; \ if (value != NULL) \ { \ staticValue = value; \ } \ if (staticValue == NULL) \ { \ staticValue = defaultValue; \ } \ if ( staticValue == nullptr ) \ { \ const char* helpMessage = "The variable in " #name "() is not initialised"; \ throw std::runtime_error( helpMessage ); \ } \ return *staticValue; \ } #define APPROVAL_TESTS_UNUSED(expr) do { (void)(expr); } while (0) #endif // ******************** From: StringUtils.h #ifndef APPROVALTESTS_CPP_STRINGUTILS_H #define APPROVALTESTS_CPP_STRINGUTILS_H class StringUtils { public: static std::string replaceAll(std::string inText, const std::string& find, const std::string& replaceWith) { size_t start_pos = 0; while ((start_pos = inText.find(find, start_pos)) != std::string::npos) { inText.replace(start_pos, find.length(), replaceWith); start_pos += replaceWith.length(); } return inText; } static bool contains(std::string inText, const std::string& find) { return inText.find(find, 0) != std::string::npos; } static std::string toLower(std::string inText) { std::string copy(inText); std::transform(inText.begin(), inText.end(), copy.begin(), [](char c){ return static_cast(::tolower(c)); }); return copy; } static bool endsWith(std::string value, std::string ending) { if (ending.size() > value.size()) { return false; } return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); } template static std::string toString(const T& contents) { std::stringstream s; s << contents; return s.str(); } }; #endif // ******************** From: ApprovalWriter.h #ifndef APPROVALTESTS_CPP_APPROVALWRITER_H #define APPROVALTESTS_CPP_APPROVALWRITER_H class ApprovalWriter { public: virtual std::string getFileExtensionWithDot() = 0; virtual void write(std::string path) = 0; virtual void cleanUpReceived(std::string receivedPath) = 0; }; #endif // ******************** From: StringWriter.h #ifndef APPROVALTESTS_CPP_STRINGWRITER_H #define APPROVALTESTS_CPP_STRINGWRITER_H class StringWriter : public ApprovalWriter { private: std::string s; std::string ext; public: StringWriter( std::string contents, std::string fileExtensionWithDot = ".txt" ) : s( contents ), ext( fileExtensionWithDot ) {} std::string getFileExtensionWithDot() override { return ext; } void write( std::string path ) override { std::ofstream out( path.c_str(), std::ofstream::out ); if ( ! out) { throw std::runtime_error("Unable to write file: " + path); } this->Write( out ); out.close(); } void Write( std::ostream &out ) { out << s << "\n"; } virtual void cleanUpReceived(std::string receivedPath) override { remove(receivedPath.c_str()); } }; #endif // ******************** From: FileUtils.h #ifndef APPROVALTESTS_CPP_FILEUTILS_H #define APPROVALTESTS_CPP_FILEUTILS_H class FileUtils { public: static bool fileExists(std::string path) { struct stat info; return stat( path.c_str(), &info ) == 0; } static int fileSize(std::string path) { struct stat statbuf; int stat_ok = stat(path.c_str(), &statbuf); if (stat_ok == -1) { return -1; } return int(statbuf.st_size); } static void ensureFileExists(std::string fullFilePath) { if (!fileExists(fullFilePath)) { StringWriter s("", ""); s.write(fullFilePath); } } static std::string getExtensionWithDot(std::string filePath) { std::size_t found = filePath.find_last_of("."); return filePath.substr(found); } static void writeToFile(std::string filePath, std::string content) { std::ofstream out(filePath.c_str(), std::ios::binary | std::ofstream::out); out << content; } }; #endif // ******************** From: WinMinGWUtils.h #ifndef APPROVALTESTS_CPP_WINMINGWUTILS_H #define APPROVALTESTS_CPP_WINMINGWUTILS_H // #if (defined(__MINGW32__) || defined(__MINGW64__)) #define APPROVAL_TESTS_MINGW #endif #ifdef APPROVAL_TESTS_MINGW #ifdef __cplusplus extern "C" { #endif #include /* errno_t, size_t */ errno_t getenv_s( size_t *ret_required_buf_size, char *buf, size_t buf_size_in_bytes, const char *name ); #ifdef __cplusplus } #endif #endif // APPROVAL_TESTS_MINGW // #endif // ******************** From: SystemUtils.h #ifndef APPROVALTESTS_CPP_SYSTEMUTILS_H #define APPROVALTESTS_CPP_SYSTEMUTILS_H // #ifdef _WIN32 // ReSharper disable once CppUnusedIncludeDirective #include #include #include #else // ReSharper disable once CppUnusedIncludeDirective #include #endif // class SystemUtils { public: static bool isWindowsOs() { #ifdef _WIN32 return true; #else return false; #endif } static bool isCygwin() { #ifdef __CYGWIN__ return true; #else return false; #endif } static std::string getDirectorySeparator() { return isWindowsOs() ? "\\" : "/"; } static std::string checkFilenameCase(const std::string& fullPath) { if (!isWindowsOs() || !FileUtils::fileExists(fullPath)) { return fullPath; } #ifdef _WIN32 WIN32_FIND_DATAA findFileData; HANDLE hFind = FindFirstFileA(fullPath.c_str(), &findFileData); if (hFind != INVALID_HANDLE_VALUE) { const std::string fixedFilename = findFileData.cFileName; const std::string fixedPath = StringUtils::replaceAll( fullPath, StringUtils::toLower(fixedFilename), fixedFilename ); FindClose(hFind); return fixedPath; } #endif return fullPath; } static std::string safeGetEnvForWindows(char const *name) { APPROVAL_TESTS_UNUSED(name); #ifdef _WIN32 size_t size; getenv_s(&size, nullptr, 0, name); if (size != 0) { std::string result; result.resize(size); getenv_s(&size, &*result.begin(), size, name); result.pop_back(); return result; } #endif return std::string(); } static std::string safeGetEnvForNonWindows(char const *name) { APPROVAL_TESTS_UNUSED(name); char* p = nullptr; #ifndef _WIN32 p = getenv(name); #endif return (p != nullptr) ? p : std::string(); } static std::string safeGetEnv(char const *name) { return isWindowsOs() ? safeGetEnvForWindows(name) : safeGetEnvForNonWindows(name); } static std::string getMachineName() { auto name = safeGetEnv("COMPUTERNAME"); if ( ! name.empty()) { return name; } name = safeGetEnv("HOSTNAME"); if ( ! name.empty()) { return name; } return "Unknown Computer"; } static void makeDirectoryForWindows(std::string directory) { APPROVAL_TESTS_UNUSED(directory); #ifdef _WIN32 int nError = _mkdir(directory.c_str()); if (nError != 0) { std::string helpMessage = std::string("Unable to create directory: ") + directory; throw std::runtime_error( helpMessage ); } #endif } static void makeDirectoryForNonWindows(std::string directory) { APPROVAL_TESTS_UNUSED(directory); #ifndef _WIN32 mode_t nMode = 0733; int nError = mkdir(directory.c_str(),nMode); if (nError != 0) { std::string helpMessage = std::string("Unable to create directory: ") + directory; throw std::runtime_error( helpMessage ); } #endif } static void makeDirectory(std::string directory) { makeDirectoryForWindows(directory); makeDirectoryForNonWindows(directory); } static void ensureDirectoryExists(std::string fullFilePath) { if (!FileUtils::fileExists(fullFilePath)) { makeDirectory(fullFilePath); } } }; #endif // ******************** From: MachineBlocker.h #ifndef APPROVALTESTS_CPP_MACHINEBLOCKER_H #define APPROVALTESTS_CPP_MACHINEBLOCKER_H class MachineBlocker : public Blocker { private: std::string machineName; bool block; MachineBlocker() = delete; public: MachineBlocker( const std::string& machineName, bool block ) : machineName(machineName), block(block) { } static MachineBlocker onMachineNamed( const std::string& machineName ) { return MachineBlocker(machineName, true); } static MachineBlocker onMachinesNotNamed( const std::string& machineName ) { return MachineBlocker(machineName, false); } virtual bool isBlockingOnThisMachine() const override { const auto isMachine = (SystemUtils::getMachineName() == machineName); return isMachine == block; } }; #endif // ******************** From: FileUtilsSystemSpecific.h #ifndef APPROVALTESTS_CPP_FILEUTILSSYSTEMSPECIFIC_H #define APPROVALTESTS_CPP_FILEUTILSSYSTEMSPECIFIC_H class FileUtilsSystemSpecific { public: static std::string getCommandLineForCopy(std::string source, std::string destination, bool isWindows) { if (isWindows) { return std::string("copy /Y ") + "\"" + source + "\" \"" + destination + "\""; } else { return std::string("cp ") + "\"" + source + "\" \"" + destination + "\""; } } static void copyFile( std::string source, std::string destination ) { system( getCommandLineForCopy(source, destination, SystemUtils::isWindowsOs()).c_str() ); } }; #endif // ******************** From: Reporter.h #ifndef APPROVALTESTS_CPP_REPORTER_H #define APPROVALTESTS_CPP_REPORTER_H class Reporter { public: virtual ~Reporter() = default; virtual bool report(std::string received, std::string approved) const = 0; }; #endif // ******************** From: AutoApproveReporter.h #ifndef APPROVALTESTS_CPP_AUTOAPPROVEREPORTER_H #define APPROVALTESTS_CPP_AUTOAPPROVEREPORTER_H class AutoApproveReporter : public Reporter { public: bool report(std::string received, std::string approved) const override { std::cout << "file " << approved << " automatically approved - next run should succeed\n"; FileUtilsSystemSpecific::copyFile( received, approved ); return true; } }; #endif // ******************** From: GoogleCustomizationsFactory.h #ifndef APPROVALTESTS_CPP_GOOGLECUSTOMIZATIONSFACTORY_H #define APPROVALTESTS_CPP_GOOGLECUSTOMIZATIONSFACTORY_H class GoogleCustomizationsFactory { public: using Comparator = std::function; private: using ComparatorContainer = std::vector< Comparator >; APPROVAL_TESTS_MACROS_STATIC(ComparatorContainer, comparatorContainer, GoogleCustomizationsFactory::createContainer()) static ComparatorContainer* createContainer() { auto container = new ComparatorContainer; auto exactNameMatching = [](std::string testFileNameWithExtension, std::string testCaseName) { return StringUtils::contains(testFileNameWithExtension, testCaseName + "."); }; container->push_back( exactNameMatching ); return container; } public: static ComparatorContainer getEquivalencyChecks() { return comparatorContainer(); } static bool addTestCaseNameRedundancyCheck(Comparator comparator) { comparatorContainer().push_back(comparator); return true; } }; #endif // ******************** From: ExistingFile.h #ifndef APPROVALTESTS_CPP_EXISTINGFILE_H #define APPROVALTESTS_CPP_EXISTINGFILE_H class ExistingFile : public ApprovalWriter{ std::string filePath; public: ExistingFile(std::string filePath) : filePath(filePath){} virtual std::string getFileExtensionWithDot() override { return FileUtils::getExtensionWithDot(filePath); } virtual void write(std::string ) override { } virtual void cleanUpReceived(std::string ) override { } }; #endif // ******************** From: CommandLauncher.h #ifndef APPROVALTESTS_CPP_COMMANDLAUNCHER_H #define APPROVALTESTS_CPP_COMMANDLAUNCHER_H class CommandLauncher { public: virtual ~CommandLauncher() {} virtual bool launch(std::vector argv) = 0; }; #endif // ******************** From: CommandReporter.h #ifndef APPROVALTESTS_CPP_COMMANDREPORTER_H #define APPROVALTESTS_CPP_COMMANDREPORTER_H class CommandReporter : public Reporter { private: std::string cmd; CommandLauncher *l; protected: CommandReporter(std::string command, CommandLauncher *launcher) : cmd(command), l(launcher) { } public: bool report(std::string received, std::string approved) const override { FileUtils::ensureFileExists(approved); return l->launch(getFullCommand(received, approved)); } std::vector getFullCommand(const std::string &received, const std::string &approved) const { std::vector fullCommand; fullCommand.push_back(cmd); fullCommand.push_back(received); fullCommand.push_back(approved); return fullCommand; } }; #endif // ******************** From: SystemLauncher.h #ifndef APPROVALTESTS_CPP_SYSTEMLAUNCHER_H #define APPROVALTESTS_CPP_SYSTEMLAUNCHER_H typedef std::vector (*ConvertArgumentsFunctionPointer)(std::vector); class SystemLauncher : public CommandLauncher { private: ConvertArgumentsFunctionPointer convertArgumentsForSystemLaunching; public: SystemLauncher() : SystemLauncher(doNothing) { } explicit SystemLauncher(std::vector (*pointer)(std::vector)) : convertArgumentsForSystemLaunching(pointer) { } void setConvertArgumentsForSystemLaunchingFunction(ConvertArgumentsFunctionPointer function) { convertArgumentsForSystemLaunching = function; } bool exists(const std::string& command) { bool foundByWhich = false; if (!SystemUtils::isWindowsOs()) { std::string which = "which " + command + " > /dev/null 2>&1"; int result = system(which.c_str()); foundByWhich = (result == 0); } return foundByWhich || FileUtils::fileExists(command); } static std::vector doNothing(std::vector argv) { return argv; } bool launch(std::vector argv) override { if (!exists(argv.front())) { return false; } argv = convertArgumentsForSystemLaunching(argv); std::string command = std::accumulate(argv.begin(), argv.end(), std::string(""), [](std::string a, std::string b) {return a + " " + "\"" + b + "\""; }); std::string launch = SystemUtils::isWindowsOs() ? ("start \"\" " + command) : (command + " &"); system(launch.c_str()); return true; } }; #endif // ******************** From: DiffInfo.h #ifndef APPROVALTESTS_CPP_DIFFINFO_H #define APPROVALTESTS_CPP_DIFFINFO_H enum class Type { TEXT, IMAGE, TEXT_AND_IMAGE }; struct DiffInfo { DiffInfo(std::string program, Type type) : program(program), arguments("%s %s"), type(type) { } DiffInfo(std::string program, std::string arguments, Type type) : program(program), arguments(arguments), type(type) { } std::string program; std::string arguments; Type type; std::string getProgramForOs() const { std::string result = program; if (result.rfind("{ProgramFiles}", 0) == 0) { const std::vector envVars = { "ProgramFiles", "ProgramW6432", "ProgramFiles(x86)" }; for(const auto& envVar : envVars) { std::string envVarValue = SystemUtils::safeGetEnv(envVar); if (envVarValue.empty()) { continue; } envVarValue += '\\'; auto result1 = StringUtils::replaceAll(result, "{ProgramFiles}", envVarValue); if (FileUtils::fileExists(result1)) { return result1; } } } return result; } }; #endif // ******************** From: DiffPrograms.h #ifndef APPROVALTESTS_CPP_DIFFPROGRAMS_H #define APPROVALTESTS_CPP_DIFFPROGRAMS_H #define APPROVAL_TESTS_MACROS_ENTRY(name, defaultValue) \ static DiffInfo name() { return defaultValue; } namespace DiffPrograms { namespace Mac { APPROVAL_TESTS_MACROS_ENTRY(DIFF_MERGE, DiffInfo("/Applications/DiffMerge.app/Contents/MacOS/DiffMerge", "%s %s -nosplash", Type::TEXT)) APPROVAL_TESTS_MACROS_ENTRY(BEYOND_COMPARE, DiffInfo("/Applications/Beyond Compare.app/Contents/MacOS/bcomp", Type::TEXT)) APPROVAL_TESTS_MACROS_ENTRY(KALEIDOSCOPE, DiffInfo("/Applications/Kaleidoscope.app/Contents/MacOS/ksdiff", Type::TEXT_AND_IMAGE)) APPROVAL_TESTS_MACROS_ENTRY(KDIFF3, DiffInfo("/Applications/kdiff3.app/Contents/MacOS/kdiff3", "%s %s -m", Type::TEXT)) APPROVAL_TESTS_MACROS_ENTRY(P4MERGE, DiffInfo("/Applications/p4merge.app/Contents/MacOS/p4merge", Type::TEXT_AND_IMAGE)) APPROVAL_TESTS_MACROS_ENTRY(TK_DIFF, DiffInfo("/Applications/TkDiff.app/Contents/MacOS/tkdiff", Type::TEXT)) APPROVAL_TESTS_MACROS_ENTRY(VS_CODE, DiffInfo("/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code", "-d %s %s", Type::TEXT)) } namespace Linux { APPROVAL_TESTS_MACROS_ENTRY(KDIFF3, DiffInfo("kdiff3", Type::TEXT)) APPROVAL_TESTS_MACROS_ENTRY(MELD, DiffInfo("meld", Type::TEXT)) } namespace Windows { APPROVAL_TESTS_MACROS_ENTRY(BEYOND_COMPARE_3, DiffInfo("{ProgramFiles}Beyond Compare 3\\BCompare.exe", Type::TEXT_AND_IMAGE)) APPROVAL_TESTS_MACROS_ENTRY(BEYOND_COMPARE_4, DiffInfo("{ProgramFiles}Beyond Compare 4\\BCompare.exe", Type::TEXT_AND_IMAGE)) APPROVAL_TESTS_MACROS_ENTRY(TORTOISE_IMAGE_DIFF, DiffInfo("{ProgramFiles}TortoiseSVN\\bin\\TortoiseIDiff.exe", "/left:%s /right:%s", Type::IMAGE)) APPROVAL_TESTS_MACROS_ENTRY(TORTOISE_TEXT_DIFF, DiffInfo("{ProgramFiles}TortoiseSVN\\bin\\TortoiseMerge.exe", Type::TEXT)) APPROVAL_TESTS_MACROS_ENTRY(WIN_MERGE_REPORTER, DiffInfo("{ProgramFiles}WinMerge\\WinMergeU.exe", Type::TEXT_AND_IMAGE)) APPROVAL_TESTS_MACROS_ENTRY(ARAXIS_MERGE, DiffInfo("{ProgramFiles}Araxis\\Araxis Merge\\Compare.exe", Type::TEXT_AND_IMAGE)) APPROVAL_TESTS_MACROS_ENTRY(CODE_COMPARE, DiffInfo("{ProgramFiles}Devart\\Code Compare\\CodeCompare.exe", Type::TEXT)) APPROVAL_TESTS_MACROS_ENTRY(KDIFF3, DiffInfo("{ProgramFiles}KDiff3\\kdiff3.exe", Type::TEXT)) APPROVAL_TESTS_MACROS_ENTRY(VS_CODE, DiffInfo("{ProgramFiles}Microsoft VS Code\\Code.exe", "-d %s %s", Type::TEXT)) } } #endif // ******************** From: GenericDiffReporter.h #ifndef APPROVALTESTS_CPP_GENERICDIFFREPORTER_H #define APPROVALTESTS_CPP_GENERICDIFFREPORTER_H class GenericDiffReporter : public CommandReporter { private: SystemLauncher launcher; public: GenericDiffReporter(const std::string& program) : CommandReporter(program, &launcher) { checkForCygwin(); } GenericDiffReporter(const DiffInfo& info) : CommandReporter(info.getProgramForOs().c_str(), &launcher) { checkForCygwin(); } void checkForCygwin() { if ( SystemUtils::isCygwin()) { launcher.setConvertArgumentsForSystemLaunchingFunction(convertForCygwin); } } static std::vector convertForCygwin(std::vector argv) { if (! SystemUtils::isCygwin()) { return argv; } std::vector copy = argv; for( size_t i = 0; i != argv.size(); ++i ) { if ( i == 0) { copy[i] = "$(cygpath '" + argv[i] + "')"; } else { copy[i] = "$(cygpath -aw '" + argv[i] + "')"; } } return copy; } }; #endif // ******************** From: FirstWorkingReporter.h #ifndef APPROVALTESTS_CPP_FIRSTWORKINGREPORTER_H #define APPROVALTESTS_CPP_FIRSTWORKINGREPORTER_H class FirstWorkingReporter : public Reporter { private: std::vector< std::unique_ptr > reporters; public: FirstWorkingReporter(std::vector theReporters) { for(auto r : theReporters) { reporters.push_back(std::unique_ptr(r)); } } bool report(std::string received, std::string approved) const override { for(auto& r : reporters) { if (r->report(received, approved)) { return true; } } return false; } }; #endif // ******************** From: LinuxReporters.h #ifndef APPROVALTESTS_CPP_LINUXREPORTERS_H #define APPROVALTESTS_CPP_LINUXREPORTERS_H namespace Linux { class KDiff3Reporter : public GenericDiffReporter { public: KDiff3Reporter() : GenericDiffReporter(DiffPrograms::Linux::KDIFF3()) {}; }; class MeldReporter : public GenericDiffReporter { public: MeldReporter() : GenericDiffReporter(DiffPrograms::Linux::MELD()) {}; }; class LinuxDiffReporter : public FirstWorkingReporter { public: LinuxDiffReporter() : FirstWorkingReporter( { new MeldReporter(), new KDiff3Reporter() } ) { } }; } #endif // ******************** From: MacReporters.h #ifndef APPROVALTESTS_CPP_MACREPORTERS_H #define APPROVALTESTS_CPP_MACREPORTERS_H namespace Mac { class DiffMergeReporter : public GenericDiffReporter { public: DiffMergeReporter() : GenericDiffReporter(DiffPrograms::Mac::DIFF_MERGE()) {} }; class VisualStudioCodeReporter : public GenericDiffReporter { public: VisualStudioCodeReporter() : GenericDiffReporter(DiffPrograms::Mac::VS_CODE()) {} }; class BeyondCompareReporter : public GenericDiffReporter { public: BeyondCompareReporter() : GenericDiffReporter(DiffPrograms::Mac::BEYOND_COMPARE()) {} }; class KaleidoscopeReporter : public GenericDiffReporter { public: KaleidoscopeReporter() : GenericDiffReporter(DiffPrograms::Mac::KALEIDOSCOPE()) {} }; class KDiff3Reporter : public GenericDiffReporter { public: KDiff3Reporter() : GenericDiffReporter(DiffPrograms::Mac::KDIFF3()) {} }; class P4MergeReporter : public GenericDiffReporter { public: P4MergeReporter() : GenericDiffReporter(DiffPrograms::Mac::P4MERGE()) {} }; class TkDiffReporter : public GenericDiffReporter { public: TkDiffReporter() : GenericDiffReporter(DiffPrograms::Mac::TK_DIFF()) {} }; class MacDiffReporter : public FirstWorkingReporter { public: MacDiffReporter() : FirstWorkingReporter( { new BeyondCompareReporter(), new DiffMergeReporter(), new KaleidoscopeReporter(), new P4MergeReporter(), new KDiff3Reporter(), new TkDiffReporter(), new VisualStudioCodeReporter() } ) { } }; } #endif // ******************** From: WindowsReporters.h #ifndef APPROVALTESTS_CPP_WINDOWSREPORTERS_H #define APPROVALTESTS_CPP_WINDOWSREPORTERS_H namespace Windows { class BeyondCompare3Reporter : public GenericDiffReporter { public: BeyondCompare3Reporter() : GenericDiffReporter(DiffPrograms::Windows::BEYOND_COMPARE_3()) {}; }; class VisualStudioCodeReporter : public GenericDiffReporter { public: VisualStudioCodeReporter() : GenericDiffReporter(DiffPrograms::Windows::VS_CODE()) {}; }; class BeyondCompare4Reporter : public GenericDiffReporter { public: BeyondCompare4Reporter() : GenericDiffReporter(DiffPrograms::Windows::BEYOND_COMPARE_4()) {}; }; class BeyondCompareReporter : public FirstWorkingReporter { public: BeyondCompareReporter() : FirstWorkingReporter({new BeyondCompare4Reporter(), new BeyondCompare3Reporter()}) { } }; class TortoiseImageDiffReporter : public GenericDiffReporter { public: TortoiseImageDiffReporter() : GenericDiffReporter(DiffPrograms::Windows::TORTOISE_IMAGE_DIFF()) {}; }; class TortoiseTextDiffReporter : public GenericDiffReporter { public: TortoiseTextDiffReporter() : GenericDiffReporter(DiffPrograms::Windows::TORTOISE_TEXT_DIFF()) {}; }; class TortoiseDiffReporter : public FirstWorkingReporter { public: TortoiseDiffReporter() : FirstWorkingReporter( {new TortoiseTextDiffReporter(), new TortoiseImageDiffReporter()}) { } }; class WinMergeReporter : public GenericDiffReporter { public: WinMergeReporter() : GenericDiffReporter(DiffPrograms::Windows::WIN_MERGE_REPORTER()) {}; }; class AraxisMergeReporter : public GenericDiffReporter { public: AraxisMergeReporter() : GenericDiffReporter(DiffPrograms::Windows::ARAXIS_MERGE()) {}; }; class CodeCompareReporter : public GenericDiffReporter { public: CodeCompareReporter() : GenericDiffReporter(DiffPrograms::Windows::CODE_COMPARE()) {}; }; class KDiff3Reporter : public GenericDiffReporter { public: KDiff3Reporter() : GenericDiffReporter(DiffPrograms::Windows::KDIFF3()) {}; }; class WindowsDiffReporter : public FirstWorkingReporter { public: WindowsDiffReporter() : FirstWorkingReporter( { new TortoiseDiffReporter(), new BeyondCompareReporter(), new WinMergeReporter(), new AraxisMergeReporter(), new CodeCompareReporter(), new KDiff3Reporter(), new VisualStudioCodeReporter(), } ) { } }; } #endif // ******************** From: DiffReporter.h #ifndef APPROVALTESTS_CPP_DIFFREPORTER_H #define APPROVALTESTS_CPP_DIFFREPORTER_H class DiffReporter : public FirstWorkingReporter { public: DiffReporter() : FirstWorkingReporter( { new Mac::MacDiffReporter(), new Linux::LinuxDiffReporter(), new Windows::WindowsDiffReporter() } ) { } }; #endif // ******************** From: DefaultReporterFactory.h #ifndef APPROVALTESTS_CPP_DEFAULTREPORTERFACTORY_H #define APPROVALTESTS_CPP_DEFAULTREPORTERFACTORY_H class DefaultReporterFactory { private: using ReporterContainer = std::vector< std::shared_ptr >; APPROVAL_TESTS_MACROS_STATIC(ReporterContainer, defaultReporterContainer, DefaultReporterFactory::createReporterContainer()) static ReporterContainer* createReporterContainer() { auto container = new ReporterContainer; container->push_back( std::make_shared()); return container; } public: static std::shared_ptr getDefaultReporter() { return defaultReporterContainer().at(0); } static void setDefaultReporter( const std::shared_ptr& reporter) { defaultReporterContainer().at(0) = reporter; } }; #endif // ******************** From: DefaultReporterDisposer.h #ifndef APPROVALTESTS_CPP_DEFAULTREPORTERDISPOSER_H #define APPROVALTESTS_CPP_DEFAULTREPORTERDISPOSER_H class DefaultReporterDisposer { private: std::shared_ptr previous_result; public: explicit DefaultReporterDisposer(const std::shared_ptr& reporter) { previous_result = DefaultReporterFactory::getDefaultReporter(); DefaultReporterFactory::setDefaultReporter(reporter); } ~DefaultReporterDisposer() { DefaultReporterFactory::setDefaultReporter(previous_result); } }; #endif // ******************** From: DefaultReporter.h #ifndef APPROVALTESTS_CPP_DEFAULTREPORTER_H #define APPROVALTESTS_CPP_DEFAULTREPORTER_H class DefaultReporter : public Reporter { public: virtual bool report(std::string received, std::string approved) const override { return DefaultReporterFactory::getDefaultReporter()->report(received, approved); } }; #endif // ******************** From: ApprovalNamer.h #ifndef APPROVALTESTS_CPP_APPROVALNAMER_H #define APPROVALTESTS_CPP_APPROVALNAMER_H using std::string; class ApprovalNamer { public: virtual ~ApprovalNamer() = default; virtual string getApprovedFile(string extensionWithDot) = 0; virtual string getReceivedFile(string extensionWithDot) = 0; }; #endif // ******************** From: ApprovalTestNamer.h #ifndef APPROVALTESTS_CPP_APPROVALTESTNAMER_H #define APPROVALTESTS_CPP_APPROVALTESTNAMER_H using std::string; class TestName { public: const string& getFileName() const { return fileName; } void setFileName(const string &file) { fileName = SystemUtils::checkFilenameCase(file); } std::vector sections; private: string fileName; }; class TestConfiguration { public: std::string subdirectory; }; class ApprovalTestNamer : public ApprovalNamer { private: public: ApprovalTestNamer() { } std::string getTestName() const { std::stringstream ext; auto test = getCurrentTest(); for (size_t i = 0; i < test.sections.size(); i++) { if (0 < i) { ext << "."; } ext << test.sections[i]; } return convertToFileName(ext.str()); } static bool isForbidden(char c) { static std::string forbiddenChars("\\/:?\"<>|' "); return std::string::npos != forbiddenChars.find(c); } static string convertToFileName(const string& fileName) { std::stringstream result; for (auto ch : fileName) { if (!isForbidden(ch)) { result << ch; } else { result << "_"; } } return result.str(); } // static TestName &getCurrentTest() { try { return currentTest(); } catch( const std::runtime_error& ) { std::string lineBreak = "************************************************************************************n"; std::string lineBuffer = "* *n"; std::string helpMessage = "nn" + lineBreak + lineBuffer + R"(* Welcome to Approval Tests. * * You have forgotten to configure your test framework for Approval Tests. * * To do this in Catch, add the following to your main.cpp: * * #define APPROVALS_CATCH * #include "ApprovalTests.hpp" * * To do this in Google Test, add the following to your main.cpp: * * #define APPROVALS_GOOGLETEST * #include "ApprovalTests.hpp" * * To do this in doctest, add the following to your main.cpp: * * #define APPROVALS_DOCTEST * #include "ApprovalTests.hpp" * * For more information, please visit: * https://github.com/approvals/ApprovalTests.cpp/blob/master/doc/GettingStarted.md )" + lineBuffer + lineBreak + 'n'; throw std::runtime_error( helpMessage ); } } // string getFileName() { return getSourceFileName(); } std::string getSourceFileName() const { auto file = getCurrentTest().getFileName(); auto start = file.rfind(SystemUtils::getDirectorySeparator()) + 1; auto end = file.rfind("."); auto fileName = file.substr(start, end - start); return convertToFileName(fileName); } string getDirectory() { auto file = getCurrentTest().getFileName(); auto end = file.rfind(SystemUtils::getDirectorySeparator()) + 1; auto directory = file.substr(0, end); if ( ! testConfiguration().subdirectory.empty() ) { directory += testConfiguration().subdirectory + SystemUtils::getDirectorySeparator(); SystemUtils::ensureDirectoryExists(directory); } return directory; } APPROVAL_TESTS_MACROS_STATIC(TestName, currentTest, NULL) APPROVAL_TESTS_MACROS_STATIC(TestConfiguration, testConfiguration, new TestConfiguration) virtual string getApprovedFile(string extensionWithDot) { return getFullFileName(".approved", extensionWithDot); } virtual string getReceivedFile(string extensionWithDot) { return getFullFileName(".received", extensionWithDot); } std::string getOutputFileBaseName() const { return getSourceFileName() + "." + getTestName(); } std::string getFullFileName(string approved, string extensionWithDot) { std::stringstream ext; ext << getDirectory() << getOutputFileBaseName() << approved << extensionWithDot; return ext.str(); } }; #endif // ******************** From: SubdirectoryDisposer.h #ifndef APPROVALTESTS_CPP_SUBDIRECTORYDISPOSER_H #define APPROVALTESTS_CPP_SUBDIRECTORYDISPOSER_H class SubdirectoryDisposer { private: std::string previous_result; public: explicit SubdirectoryDisposer(std::string subdirectory) { previous_result = ApprovalTestNamer::testConfiguration().subdirectory; ApprovalTestNamer::testConfiguration().subdirectory = subdirectory; } ~SubdirectoryDisposer() { ApprovalTestNamer::testConfiguration().subdirectory = previous_result; } }; #endif // ******************** From: ExistingFileNamer.h #ifndef APPROVALTESTS_CPP_EXISTINGFILENAMER_H #define APPROVALTESTS_CPP_EXISTINGFILENAMER_H class ExistingFileNamer: public ApprovalNamer{ ApprovalTestNamer namer; std::string filePath; public: ExistingFileNamer(std::string filePath): filePath(filePath){ } virtual string getApprovedFile(string extensionWithDot) { return namer.getApprovedFile(extensionWithDot); } virtual string getReceivedFile(string ) { return filePath; } }; #endif // ******************** From: DefaultFrontLoadedReporter.h #ifndef APPROVALTESTS_CPP_DEFAULTFRONTLOADEDREPORTER_H #define APPROVALTESTS_CPP_DEFAULTFRONTLOADEDREPORTER_H class DefaultFrontLoadedReporter : public Reporter { public: virtual bool report(std::string , std::string ) const override { return false; } }; #endif // ******************** From: FrontLoadedReporterFactory.h #ifndef APPROVALTESTS_CPP_FRONTLOADEDREPORTERFACTORY_H #define APPROVALTESTS_CPP_FRONTLOADEDREPORTERFACTORY_H class FrontLoadedReporterFactory { using ReporterContainer = std::vector< std::shared_ptr >; APPROVAL_TESTS_MACROS_STATIC(ReporterContainer, frontLoadedReporterContainer, FrontLoadedReporterFactory::createReporterContainer()) static ReporterContainer* createReporterContainer() { auto container = new ReporterContainer; container->push_back( std::make_shared()); return container; } public: static std::shared_ptr getFrontLoadedReporter() { return frontLoadedReporterContainer().at(0); } static void setFrontLoadedReporter( const std::shared_ptr& reporter) { frontLoadedReporterContainer().at(0) = reporter; } }; #endif // ******************** From: FrontLoadedReporterDisposer.h #ifndef APPROVALTESTS_CPP_FRONTLOADEDREPORTERDISPOSER_H #define APPROVALTESTS_CPP_FRONTLOADEDREPORTERDISPOSER_H class FrontLoadedReporterDisposer { private: std::shared_ptr previous_result; public: explicit FrontLoadedReporterDisposer(const std::shared_ptr& reporter) { previous_result = FrontLoadedReporterFactory::getFrontLoadedReporter(); FrontLoadedReporterFactory::setFrontLoadedReporter(reporter); } ~FrontLoadedReporterDisposer() { FrontLoadedReporterFactory::setFrontLoadedReporter(previous_result); } }; #endif // ******************** From: ApprovalComparator.h #ifndef APPROVALTESTS_CPP_APPROVALCOMPARATOR_H #define APPROVALTESTS_CPP_APPROVALCOMPARATOR_H class ApprovalComparator { public: virtual ~ApprovalComparator() = default; virtual bool contentsAreEquivalent(std::string receivedPath, std::string approvedPath) const = 0; }; #endif // ******************** From: TextFileComparator.h #ifndef APPROVALTESTS_CPP_TEXTFILECOMPARATOR_H #define APPROVALTESTS_CPP_TEXTFILECOMPARATOR_H class TextFileComparator : public ApprovalComparator { public: static std::ifstream::int_type getNextRelevantCharacter(std::ifstream& astream) { auto ch = astream.get(); if (ch == '\r') { return astream.get(); } else { return ch; } } virtual bool contentsAreEquivalent(std::string receivedPath, std::string approvedPath) const { std::ifstream astream(approvedPath.c_str(), std::ios::binary | std::ifstream::in); std::ifstream rstream(receivedPath.c_str(), std::ios::binary | std::ifstream::in); while (astream.good() && rstream.good()) { int a = getNextRelevantCharacter(astream); int r = getNextRelevantCharacter(rstream); if (a != r) { return false; } } return true; } }; #endif // ******************** From: ApprovalException.h #ifndef APPROVALTESTS_CPP_APPROVALEXCEPTION_H #define APPROVALTESTS_CPP_APPROVALEXCEPTION_H class ApprovalException : public std::exception { private: std::string message; public: ApprovalException( const std::string& msg ) : message( msg ) {} virtual const char *what() const throw() { return message.c_str(); } }; class ApprovalMismatchException : public ApprovalException { private: std::string format( const std::string &received, const std::string &approved ) { std::stringstream s; s << "Failed Approval: \n" << "Received does not match approved \n" << "Received : \"" << received << "\" \n" << "Approved : \"" << approved << "\""; return s.str(); } public: ApprovalMismatchException( std::string received, std::string approved ) : ApprovalException( format( received, approved ) ) { } }; class ApprovalMissingException : public ApprovalException { private: std::string format( const std::string &file ) { std::stringstream s; s << "Failed Approval: \n" << "Approval File Not Found \n" << "File: \"" << file << '"'; return s.str(); } public: ApprovalMissingException( std::string , std::string approved ) : ApprovalException( format( approved ) ) { } }; #endif // ******************** From: FileApprover.h #ifndef APPROVALTESTS_CPP_FILEAPPROVER_H #define APPROVALTESTS_CPP_FILEAPPROVER_H class FileApprover { private: using ComparatorContainer = std::map< std::string, std::shared_ptr >; APPROVAL_TESTS_MACROS_STATIC(ComparatorContainer, comparators, new ComparatorContainer()) public: FileApprover() {}; ~FileApprover() {}; static void registerComparator(std::string extensionWithDot, std::shared_ptr comparator) { comparators()[extensionWithDot] = comparator; } static std::shared_ptr getComparatorForFile(string receivedPath) { const std::string fileExtension = FileUtils::getExtensionWithDot(receivedPath); auto iterator = comparators().find(fileExtension); if (iterator != comparators().end()) { return iterator->second; } return std::make_shared(); } static void verify(std::string receivedPath, std::string approvedPath, const ApprovalComparator& comparator) { if (!FileUtils::fileExists(approvedPath)) { throw ApprovalMissingException(receivedPath, approvedPath); } if (!FileUtils::fileExists(receivedPath)) { throw ApprovalMissingException(approvedPath, receivedPath); } if (!comparator.contentsAreEquivalent(receivedPath, approvedPath)) { throw ApprovalMismatchException(receivedPath, approvedPath); } } static void verify(std::string receivedPath, std::string approvedPath) { verify(receivedPath, approvedPath, *getComparatorForFile(receivedPath)); } static void verify(ApprovalNamer& n, ApprovalWriter& s, const Reporter& r) { std::string approvedPath = n.getApprovedFile(s.getFileExtensionWithDot()); std::string receivedPath = n.getReceivedFile(s.getFileExtensionWithDot()); s.write(receivedPath); try { verify(receivedPath, approvedPath); s.cleanUpReceived(receivedPath); } catch (const ApprovalException&) { reportAfterTryingFrontLoadedReporter(receivedPath, approvedPath, r); throw; } } static void reportAfterTryingFrontLoadedReporter(const string &receivedPath, const string &approvedPath, const Reporter &r) { auto tryFirst = FrontLoadedReporterFactory::getFrontLoadedReporter(); if (!tryFirst->report(receivedPath, approvedPath)) { r.report(receivedPath, approvedPath); } } }; #endif // ******************** From: Approvals.h #ifndef APPROVALTESTS_CPP_APPROVALS_H #define APPROVALTESTS_CPP_APPROVALS_H class Approvals { private: Approvals() {} ~Approvals() {} public: static std::shared_ptr getDefaultNamer() { return std::make_shared(); } static void verify(std::string contents, const Reporter &reporter = DefaultReporter()) { StringWriter writer(contents); ApprovalTestNamer namer; FileApprover::verify(namer, writer, reporter); } template static void verify(const T& contents, const Reporter &reporter = DefaultReporter()) { verify(StringUtils::toString(contents), reporter); } template static void verify(const T& contents, std::function converter, const Reporter &reporter = DefaultReporter()) { std::stringstream s; converter(contents, s); verify(s.str(), reporter); } static void verifyExceptionMessage( std::function functionThatThrows, const Reporter &reporter = DefaultReporter()) { std::string message = "*** no exception thrown ***"; try { functionThatThrows(); } catch(const std::exception& e) { message = e.what(); } Approvals::verify(message, reporter); } template static void verifyAll(std::string header, const Iterator &start, const Iterator &finish, std::function converter, const Reporter &reporter = DefaultReporter()) { std::stringstream s; if (!header.empty()) { s << header << "\n\n\n"; } for (auto it = start; it != finish; ++it) { converter(*it, s); s << '\n'; } verify(s.str(), reporter); } template static void verifyAll(std::string header, const Container &list, std::function converter, const Reporter &reporter = DefaultReporter()) { verifyAll(header, list.begin(), list.end(), converter, reporter); } template static void verifyAll(std::string header, const std::vector &list, const Reporter &reporter = DefaultReporter()) { int i = 0; verifyAll>(header, list, [&](T e, std::ostream &s) { s << "[" << i++ << "] = " << e; }, reporter); } template static void verifyAll(const std::vector &list, const Reporter &reporter = DefaultReporter()) { verifyAll("", list, reporter); } static void verifyExistingFile(const std::string filePath, const Reporter &reporter = DefaultReporter()) { ExistingFile writer(filePath); ExistingFileNamer namer(filePath); FileApprover::verify(namer, writer, reporter); } static SubdirectoryDisposer useApprovalsSubdirectory(std::string subdirectory = "approval_tests") { return SubdirectoryDisposer(subdirectory); } static DefaultReporterDisposer useAsDefaultReporter(const std::shared_ptr& reporter) { return DefaultReporterDisposer(reporter); } static FrontLoadedReporterDisposer useAsFrontLoadedReporter(const std::shared_ptr& reporter) { return FrontLoadedReporterDisposer(reporter); } }; #endif // ******************** From: Catch2Approvals.h #ifndef APPROVALTESTS_CPP_CATCH2APPROVALS_H #define APPROVALTESTS_CPP_CATCH2APPROVALS_H // #ifdef APPROVALS_CATCH #define CATCH_CONFIG_MAIN #include struct Catch2ApprovalListener : Catch::TestEventListenerBase { using TestEventListenerBase::TestEventListenerBase; TestName currentTest; Catch2ApprovalListener(Catch::ReporterConfig const &_config) : Catch::TestEventListenerBase(_config) {} virtual void testCaseStarting(Catch::TestCaseInfo const &testInfo) override { currentTest.setFileName(testInfo.lineInfo.file); ApprovalTestNamer::currentTest(¤tTest); } virtual void testCaseEnded(Catch::TestCaseStats const &/*testCaseStats*/) override { while (!currentTest.sections.empty()) { currentTest.sections.pop_back(); } } virtual void sectionStarting(Catch::SectionInfo const §ionInfo) override { currentTest.sections.push_back(sectionInfo.name); } virtual void sectionEnded(Catch::SectionStats const &/*sectionStats*/) override { currentTest.sections.pop_back(); } }; CATCH_REGISTER_LISTENER(Catch2ApprovalListener) #endif #ifdef TEST_COMMIT_REVERT_CATCH struct Catch2TestCommitRevert : Catch::TestEventListenerBase { using TestEventListenerBase::TestEventListenerBase; virtual void testRunEnded( Catch::TestRunStats const& testRunStats )override{ bool commit = testRunStats.totals.testCases.allOk(); std::string message = "r "; if (commit) { std::cout << "git add -A n"; std::cout << "git commit -m " << message; } else { std::cout << "git clean -fd n"; std::cout << "git reset --hard HEAD n"; } } }; CATCH_REGISTER_LISTENER(Catch2TestCommitRevert) #endif // #endif // ******************** From: CombinationApprovals.h #ifndef APPROVALTESTS_CPP_COMBINATIONAPPROVALS_H #define APPROVALTESTS_CPP_COMBINATIONAPPROVALS_H class Empty { public: template< typename Other> bool operator!=(const Other &) const { return true; } bool operator!=(const Empty &) const { return false; } friend std::ostream &operator<<(std::ostream &os, const Empty&) { os << "This should never be written - see Empty\n"; return os; } }; class CombinationApprovals { public: CombinationApprovals() = delete; ~CombinationApprovals() = delete; template < typename Function, typename Container1, typename Container2, typename Container3, typename Container4, typename Container5, typename Container6, typename Container7, typename Container8, typename Container9, typename std::enable_if::value, int>::type = 0> static void verifyAllCombinations( Function converter, const Container1& inputs1, const Container2& inputs2, const Container3& inputs3, const Container4& inputs4, const Container5& inputs5, const Container6& inputs6, const Container7& inputs7, const Container8& inputs8, const Container9& inputs9, const Reporter& reporter = DefaultReporter()) { Empty empty; std::stringstream s; for (auto input1 : inputs1) { for (auto input2 : inputs2) { for (auto input3 : inputs3) { for (auto input4 : inputs4) { for (auto input5 : inputs5) { for (auto input6 : inputs6) { for (auto input7 : inputs7) { for (auto input8 : inputs8) { for (auto input9 : inputs9) { s << "(" << input1; if (empty != input2) { s << ", " << input2; } if (empty != input3) { s << ", " << input3; } if (empty != input4) { s << ", " << input4; } if (empty != input5) { s << ", " << input5; } if (empty != input6) { s << ", " << input6; } if (empty != input7) { s << ", " << input7; } if (empty != input8) { s << ", " << input8; } if (empty != input9) { s << ", " << input9; } s << ") => " << converter(input1, input2, input3, input4, input5, input6, input7, input8, input9) << '\n'; } } } } } } } } } Approvals::verify(s.str(), reporter); } template < typename Function, typename Container1, typename Container2, typename Container3, typename Container4, typename Container5, typename Container6, typename Container7, typename Container8, typename std::enable_if::value, int>::type = 0> static void verifyAllCombinations( Function converter, const Container1& inputs1, const Container2& inputs2, const Container3& inputs3, const Container4& inputs4, const Container5& inputs5, const Container6& inputs6, const Container7& inputs7, const Container8& inputs8, const Reporter& reporter = DefaultReporter()) { verifyAllCombinations( [&](typename Container1::value_type i1, typename Container2::value_type i2, typename Container3::value_type i3, typename Container4::value_type i4, typename Container5::value_type i5, typename Container6::value_type i6, typename Container7::value_type i7, typename Container8::value_type i8, Empty){return converter(i1, i2, i3, i4, i5, i6, i7, i8);}, inputs1, inputs2, inputs3, inputs4, inputs5, inputs6, inputs7, inputs8, empty(), reporter); } template < typename Function, typename Container1, typename Container2, typename Container3, typename Container4, typename Container5, typename Container6, typename Container7, typename std::enable_if::value, int>::type = 0> static void verifyAllCombinations( Function converter, const Container1& inputs1, const Container2& inputs2, const Container3& inputs3, const Container4& inputs4, const Container5& inputs5, const Container6& inputs6, const Container7& inputs7, const Reporter& reporter = DefaultReporter()) { verifyAllCombinations( [&](typename Container1::value_type i1, typename Container2::value_type i2, typename Container3::value_type i3, typename Container4::value_type i4, typename Container5::value_type i5, typename Container6::value_type i6, typename Container7::value_type i7, Empty){return converter(i1, i2, i3, i4, i5, i6, i7);}, inputs1, inputs2, inputs3, inputs4, inputs5, inputs6, inputs7, empty(), reporter); } template < typename Function, typename Container1, typename Container2, typename Container3, typename Container4, typename Container5, typename Container6, typename std::enable_if::value, int>::type = 0> static void verifyAllCombinations( Function converter, const Container1& inputs1, const Container2& inputs2, const Container3& inputs3, const Container4& inputs4, const Container5& inputs5, const Container6& inputs6, const Reporter& reporter = DefaultReporter()) { verifyAllCombinations( [&](typename Container1::value_type i1, typename Container2::value_type i2, typename Container3::value_type i3, typename Container4::value_type i4, typename Container5::value_type i5, typename Container6::value_type i6, Empty){return converter(i1, i2, i3, i4, i5, i6);}, inputs1, inputs2, inputs3, inputs4, inputs5, inputs6, empty(), reporter); } template < typename Function, typename Container1, typename Container2, typename Container3, typename Container4, typename Container5, typename std::enable_if::value, int>::type = 0> static void verifyAllCombinations( Function converter, const Container1& inputs1, const Container2& inputs2, const Container3& inputs3, const Container4& inputs4, const Container5& inputs5, const Reporter& reporter = DefaultReporter()) { verifyAllCombinations( [&](typename Container1::value_type i1, typename Container2::value_type i2, typename Container3::value_type i3, typename Container4::value_type i4, typename Container5::value_type i5, Empty){return converter(i1, i2, i3, i4, i5);}, inputs1, inputs2, inputs3, inputs4, inputs5, empty(), reporter); } template < typename Function, typename Container1, typename Container2, typename Container3, typename Container4, typename std::enable_if::value, int>::type = 0> static void verifyAllCombinations( Function converter, const Container1& inputs1, const Container2& inputs2, const Container3& inputs3, const Container4& inputs4, const Reporter& reporter = DefaultReporter()) { verifyAllCombinations( [&](typename Container1::value_type i1, typename Container2::value_type i2, typename Container3::value_type i3, typename Container4::value_type i4, Empty){return converter(i1, i2, i3, i4);}, inputs1, inputs2, inputs3, inputs4, empty(), reporter); } template < typename Function, typename Container1, typename Container2, typename Container3, typename std::enable_if::value, int>::type = 0> static void verifyAllCombinations( Function converter, const Container1& inputs1, const Container2& inputs2, const Container3& inputs3, const Reporter& reporter = DefaultReporter()) { verifyAllCombinations( [&](typename Container1::value_type i1, typename Container2::value_type i2, typename Container3::value_type i3, Empty){return converter(i1, i2, i3);}, inputs1, inputs2, inputs3, empty(), reporter); } template < typename Function, typename Container1, typename Container2, typename std::enable_if::value, int>::type = 0> static void verifyAllCombinations( Function converter, const Container1& inputs1, const Container2& inputs2, const Reporter& reporter = DefaultReporter()) { verifyAllCombinations( [&](typename Container1::value_type i1, typename Container2::value_type i2, Empty){return converter(i1, i2);}, inputs1, inputs2, empty(), reporter); } template < typename Function, typename Container1, typename std::enable_if::value, int>::type = 0> static void verifyAllCombinations( Function converter, const Container1& inputs1, const Reporter& reporter = DefaultReporter()) { verifyAllCombinations( [&](typename Container1::value_type i1, Empty){return converter(i1);}, inputs1, empty(), reporter); } using EmptyContainer = std::vector; static EmptyContainer empty() { return EmptyContainer{Empty()}; } }; #endif // ******************** From: GoogleTestApprovals.h #ifndef APPROVALTESTS_CPP_GOOGLTESTAPPPROVALS_H #define APPROVALTESTS_CPP_GOOGLTESTAPPPROVALS_H #ifdef APPROVALS_GOOGLETEST_EXISTING_MAIN #define APPROVALS_GOOGLETEST #endif #ifdef APPROVALS_GOOGLETEST // #include "gtest/gtest.h" class GoogleTestListener : public ::testing::EmptyTestEventListener { TestName currentTest; public: bool isDuplicate(std::string testFileNameWithExtension, std::string testCaseName) { for( auto check : GoogleCustomizationsFactory::getEquivalencyChecks()) { if (check(testFileNameWithExtension, testCaseName)) { return true; } } return false; } virtual void OnTestStart(const ::testing::TestInfo& testInfo) override { currentTest.setFileName(testInfo.file()); currentTest.sections = {}; if (! isDuplicate(currentTest.getFileName(), testInfo.test_case_name())) { currentTest.sections.push_back(testInfo.test_case_name()); } if (! std::string(testInfo.name()).empty()) { currentTest.sections.push_back(testInfo.name()); } ApprovalTestNamer::currentTest(¤tTest); } }; inline void initializeApprovalTestsForGoogleTests() { auto& listeners = testing::UnitTest::GetInstance()->listeners(); listeners.Append(new GoogleTestListener); } #ifndef APPROVALS_GOOGLETEST_EXISTING_MAIN int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); initializeApprovalTestsForGoogleTests(); return RUN_ALL_TESTS(); } #endif //APPROVALS_GOOGLETEST_EXISTING_MAIN // #endif #endif // ******************** From: DocTestApprovals.h #ifndef APPROVALTESTS_CPP_DOCTESTAPPROVALS_H #define APPROVALTESTS_CPP_DOCTESTAPPROVALS_H // #ifdef APPROVALS_DOCTEST #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include // anonymous namespace to prevent compiler -Wsubobject-linkage warnings // This is OK as this code is only compiled on main() namespace { struct AbstractReporter : doctest::IReporter { virtual void report_query(const doctest::QueryData&) {} // called when the whole test run starts virtual void test_run_start() {} // called when the whole test run ends (caching a pointer to the input doesn't make sense here) virtual void test_run_end(const doctest::TestRunStats &) {} // called when a test case is started (safe to cache a pointer to the input) virtual void test_case_start(const doctest::TestCaseData &) {} // called when a test case has ended virtual void test_case_end(const doctest::CurrentTestCaseStats &) {} // called when an exception is thrown from the test case (or it crashes) virtual void test_case_exception(const doctest::TestCaseException &) {} // called whenever a subcase is entered (don't cache pointers to the input) virtual void subcase_start(const doctest::SubcaseSignature &) {} // called whenever a subcase is exited (don't cache pointers to the input) virtual void subcase_end() {} // called for each assert (don't cache pointers to the input) virtual void log_assert(const doctest::AssertData &) {} // called for each message (don't cache pointers to the input) virtual void log_message(const doctest::MessageData &) {} // called when a test case is skipped either because it doesn't pass the filters, has a skip decorator // or isn't in the execution range (between first and last) (safe to cache a pointer to the input) virtual void test_case_skipped(const doctest::TestCaseData &) {} }; struct DocTestApprovalListener : AbstractReporter { TestName currentTest; // constructor has to accept the ContextOptions by ref as a single argument DocTestApprovalListener(const doctest::ContextOptions & /*in*/) { } void test_case_start(const doctest::TestCaseData &testInfo) override { currentTest.sections.push_back(testInfo.m_name); currentTest.setFileName(testInfo.m_file); ApprovalTestNamer::currentTest(¤tTest); } void test_case_end(const doctest::CurrentTestCaseStats & /*in*/) override { while (!currentTest.sections.empty()) { currentTest.sections.pop_back(); } } void subcase_start(const doctest::SubcaseSignature &signature) override { currentTest.sections.push_back(signature.m_name); } void subcase_end() override { currentTest.sections.pop_back(); } }; } REGISTER_LISTENER("approvals", 0, DocTestApprovalListener); #endif // APPROVALS_DOCTEST // #endif // ******************** From: GoogleConfiguration.h #ifndef APPROVALTESTS_CPP_GOOGLECONFIGURATION_H #define APPROVALTESTS_CPP_GOOGLECONFIGURATION_H class GoogleConfiguration { public: static bool addTestCaseNameRedundancyCheck(GoogleCustomizationsFactory::Comparator comparator) { return GoogleCustomizationsFactory::addTestCaseNameRedundancyCheck(comparator); } static bool addIgnorableTestCaseNameSuffix(std::string suffix) { return addTestCaseNameRedundancyCheck( createIgnorableTestCaseNameSuffixCheck(suffix) ); } static GoogleCustomizationsFactory::Comparator createIgnorableTestCaseNameSuffixCheck( const std::string& suffix ) { return [suffix](std::string testFileNameWithExtension, std::string testCaseName) { if (testCaseName.length() <= suffix.length() || !StringUtils::endsWith(testCaseName, suffix)) { return false; } auto withoutSuffix = testCaseName.substr(0, testCaseName.length() - suffix.length()); auto withFileExtension = withoutSuffix + "."; return StringUtils::contains(testFileNameWithExtension, withFileExtension); }; } }; #endif // ******************** From: OkraApprovals.h #ifndef APPROVALTESTS_CPP_OKRAAPPPROVALS_H #define APPROVALTESTS_CPP_OKRAAPPPROVALS_H #ifdef APPROVALS_OKRA // #include "Okra.h" struct OkraApprovalListener : public okra::IListener { TestName currentTest; public: void OnStart(const okra::TestInfo &testInfo) override { currentTest.fileName = testInfo.file_path; currentTest.sections = {testInfo.name}; ApprovalTestNamer::currentTest(¤tTest); } void OnEnd(const okra::TestInfo &testInfo, std::chrono::high_resolution_clock::duration duration) override { } void OnFail(const std::string &message) override { } }; OKRA_REGISTER_LISTENER(OkraApprovalListener); // #endif #endif // ******************** From: AutoApproveIfMissingReporter.h #ifndef APPROVALTESTS_CPP_AUTOAPPROVEIFMISSINGREPORTER_H #define APPROVALTESTS_CPP_AUTOAPPROVEIFMISSINGREPORTER_H class AutoApproveIfMissingReporter : public Reporter { public: bool report(std::string received, std::string approved) const override { if (FileUtils::fileExists(approved)) { return false; } return AutoApproveReporter().report(received, approved); } }; #endif // ******************** From: BlockingReporter.h #ifndef APPROVALTESTS_CPP_BLOCKINGREPORTER_H #define APPROVALTESTS_CPP_BLOCKINGREPORTER_H class BlockingReporter : public Reporter { private: std::shared_ptr blocker; BlockingReporter() = delete; public: BlockingReporter( std::shared_ptr blocker ) : blocker(blocker) { } static std::shared_ptr onMachineNamed( const std::string& machineName ) { auto machineBlocker = std::make_shared( MachineBlocker::onMachineNamed(machineName) ); return std::make_shared(machineBlocker); } static std::shared_ptr onMachinesNotNamed( const std::string& machineName ) { auto machineBlocker = std::make_shared( MachineBlocker::onMachinesNotNamed(machineName) ); return std::make_shared(machineBlocker); } virtual bool report(std::string , std::string ) const override { return blocker->isBlockingOnThisMachine(); } }; #endif // ******************** From: ClipboardReporter.h #ifndef APPROVALTESTS_CPP_COMMANDLINEREPORTER_H #define APPROVALTESTS_CPP_COMMANDLINEREPORTER_H class ClipboardReporter : public Reporter { public: static std::string getCommandLineFor(std::string received, std::string approved, bool isWindows) { if (isWindows) { return std::string("move /Y ") + "\"" + received + "\" \"" + approved + "\""; } else { return std::string("mv ") + "\"" + received + "\" \"" + approved + "\""; } } virtual bool report(std::string received, std::string approved) const override { copyToClipboard(getCommandLineFor(received, approved, SystemUtils::isWindowsOs())); return true; } static void copyToClipboard(const std::string& newClipboard) { const std::string clipboardCommand = SystemUtils::isWindowsOs() ? "clip" : "pbclip"; auto cmd = std::string("echo ") + newClipboard + " | " + clipboardCommand; system(cmd.c_str()); } }; #endif // ******************** From: CombinationReporter.h #ifndef APPROVALTESTS_CPP_COMBINATIONREPORTER_H #define APPROVALTESTS_CPP_COMBINATIONREPORTER_H class CombinationReporter : public Reporter { private: std::vector< std::unique_ptr > reporters; public: CombinationReporter(std::vector theReporters) { for(auto r : theReporters) { reporters.push_back(std::unique_ptr(r)); } } bool report(std::string received, std::string approved) const override { bool result = false; for(auto& r : reporters) { result |= r->report(received, approved); } return result; } }; #endif // ******************** From: QuietReporter.h #ifndef APPROVALTESTS_CPP_QUIETREPORTER_H #define APPROVALTESTS_CPP_QUIETREPORTER_H class QuietReporter : public Reporter { public: bool report(std::string , std::string ) const override { return true; } }; #endif