/**************************************************************************** ** Copyright (c) 2021, Fougue Ltd. ** All rights reserved. ** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt ****************************************************************************/ #include "test_base.h" #include "test_measure.h" #include "test_app.h" #include #include #include #include namespace Mayo { namespace { // Helper function to return the path of some temporary file // The file path should be unique just after function call QString getTemporaryFilePath() { QTemporaryFile file; if (file.open()) return file.fileName(); return {}; } // Helper function to check equality between two C strings bool cstringEqual(const char* lhs, const char* rhs) { return std::strcmp(lhs, rhs) == 0; } // Helper struct to hold the output filepath and format specified in command line struct OutputFile { QString path; QString format; }; // Retrieve the filename and format from string 'optionCmdLine' // The 'optionCmdLine' argument should be the option specified in command line just after '-o' // For example: // $> mayo --runtests -o filename,format // $> mayo --runtests -o filename OutputFile parseOutputFile(const QString& optionCmdLine) { OutputFile result; const int posComma = optionCmdLine.lastIndexOf(','); if (posComma != -1) { result.path = optionCmdLine.left(posComma); result.format = optionCmdLine.right(optionCmdLine.size() - posComma); } else { result.path = optionCmdLine; } return result; } } // namespace int runTests(int argc, char* argv[]) { // Preprocess command-line arguments QStringList args; QString* ptrArgOutputFileName = nullptr; for (int i = 0; i < argc; ++i) { // Don't keep "--runtests" argument because QText::qExec() will reject it if (!cstringEqual(argv[i], "--runtests")) args.push_back(QString::fromUtf8(argv[i])); // Keep track of the output filename argument(specified after "-o" option) if (i > 0 && cstringEqual(argv[i-1], "-o") && !args.empty() && !args.back().startsWith('-')) ptrArgOutputFileName = &args.back(); } // Retrieve the output filename and format(separated by comma, eg "filename,format") const QString argOutputFileName = ptrArgOutputFileName ? *ptrArgOutputFileName : QString{}; const OutputFile argOutputFile = parseOutputFile(argOutputFileName); // Declare unit tests to be checked std::vector> vecTest; vecTest.emplace_back(new Mayo::TestBase); vecTest.emplace_back(new Mayo::TestMeasure); vecTest.emplace_back(new Mayo::TestApp); // Execute unit tests // As QText::qExec() is called for each test object, it would overwrite any output file // specified with "-o filename,format" // This is solved by substituing output file argument with a temporary file whose contents // is appended to the target output file int retcode = 0; for (const std::unique_ptr& test : vecTest) { // Replace the output file argument with a temporary file path const QString outputTestFileName = ptrArgOutputFileName ? getTemporaryFilePath() : QString{}; if (ptrArgOutputFileName) *ptrArgOutputFileName = outputTestFileName + argOutputFile.format; // Execute test retcode += QTest::qExec(test.get(), args); // Append the temporary file to the target output file if (ptrArgOutputFileName) { const bool isFirstTest = &test == &vecTest.front(); QFile outputTestFile(outputTestFileName); QFile outputFile(argOutputFile.path); outputTestFile.open(QIODevice::ReadOnly); outputFile.open(QIODevice::WriteOnly | (isFirstTest ? QIODevice::NotOpen : QIODevice::Append)); outputFile.write(outputTestFile.readAll()); } } return retcode; } } // namespace Mayo