From eef13b63ddce45eba0e3aa77d01d49e7796fbe10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Tue, 6 Feb 2024 20:55:56 +0100 Subject: [PATCH] Fix #12411 (GUI: product name and version in xml) (#5947) --- gui/mainwindow.cpp | 6 +++--- gui/resultstree.cpp | 2 +- gui/resultsview.cpp | 6 +++--- gui/resultsview.h | 3 ++- gui/test/xmlreportv2/testxmlreportv2.cpp | 2 +- gui/xmlreportv2.cpp | 12 ++++++++++-- gui/xmlreportv2.h | 5 ++++- lib/errorlogger.cpp | 11 +++-------- lib/settings.cpp | 22 ++++++++++++++++++++++ lib/settings.h | 2 ++ test/testsettings.cpp | 16 ++++++++++++++++ 11 files changed, 67 insertions(+), 20 deletions(-) diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 2a422594988..5695fe02823 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -1166,7 +1166,7 @@ void MainWindow::analysisDone() if (QDir(buildDir).exists()) { mUI->mResults->saveStatistics(buildDir + "/statistics.txt"); mUI->mResults->updateFromOldReport(buildDir + "/lastResults.xml"); - mUI->mResults->save(buildDir + "/lastResults.xml", Report::XMLV2); + mUI->mResults->save(buildDir + "/lastResults.xml", Report::XMLV2, mCppcheckCfgProductName); } } @@ -1564,7 +1564,7 @@ void MainWindow::save() type = Report::CSV; } - mUI->mResults->save(selectedFile, type); + mUI->mResults->save(selectedFile, type, mCppcheckCfgProductName); setPath(SETTINGS_LAST_RESULT_PATH, selectedFile); } } @@ -1585,7 +1585,7 @@ void MainWindow::complianceReport() tempResults.open(); tempResults.close(); - mUI->mResults->save(tempResults.fileName(), Report::XMLV2); + mUI->mResults->save(tempResults.fileName(), Report::XMLV2, mCppcheckCfgProductName); ComplianceReportDialog dlg(mProjectFile, tempResults.fileName()); dlg.exec(); diff --git a/gui/resultstree.cpp b/gui/resultstree.cpp index 8f132c25967..cfefd73ceb3 100644 --- a/gui/resultstree.cpp +++ b/gui/resultstree.cpp @@ -1231,7 +1231,7 @@ static int indexOf(const QList &list, const ErrorItem &item) void ResultsTree::updateFromOldReport(const QString &filename) { QList oldErrors; - XmlReportV2 oldReport(filename); + XmlReportV2 oldReport(filename, QString()); if (oldReport.open()) { oldErrors = oldReport.read(); oldReport.close(); diff --git a/gui/resultsview.cpp b/gui/resultsview.cpp index 402cab520fe..beb6d60a1fa 100644 --- a/gui/resultsview.cpp +++ b/gui/resultsview.cpp @@ -204,7 +204,7 @@ void ResultsView::updateFromOldReport(const QString &filename) const mUI->mTree->updateFromOldReport(filename); } -void ResultsView::save(const QString &filename, Report::Type type) const +void ResultsView::save(const QString &filename, Report::Type type, const QString& productName) const { Report *report = nullptr; @@ -216,7 +216,7 @@ void ResultsView::save(const QString &filename, Report::Type type) const report = new TxtReport(filename); break; case Report::XMLV2: - report = new XmlReportV2(filename); + report = new XmlReportV2(filename, productName); break; } @@ -408,7 +408,7 @@ void ResultsView::readErrorsXml(const QString &filename) return; } - XmlReportV2 report(filename); + XmlReportV2 report(filename, QString()); QList errors; if (report.open()) { errors = report.read(); diff --git a/gui/resultsview.h b/gui/resultsview.h index d978afab936..6a9743b2889 100644 --- a/gui/resultsview.h +++ b/gui/resultsview.h @@ -86,8 +86,9 @@ class ResultsView : public QWidget { * * @param filename Filename to save results to * @param type Type of the report. + * @param productName Custom product name */ - void save(const QString &filename, Report::Type type) const; + void save(const QString &filename, Report::Type type, const QString& productName) const; /** * @brief Update results from old report (tag, sinceDate) diff --git a/gui/test/xmlreportv2/testxmlreportv2.cpp b/gui/test/xmlreportv2/testxmlreportv2.cpp index 25b822718f4..cede5842de0 100644 --- a/gui/test/xmlreportv2/testxmlreportv2.cpp +++ b/gui/test/xmlreportv2/testxmlreportv2.cpp @@ -27,7 +27,7 @@ void TestXmlReportV2::readXml() const { const QString filepath(QString(SRCDIR) + "/../data/xmlfiles/xmlreport_v2.xml"); - XmlReportV2 report(filepath); + XmlReportV2 report(filepath, QString()); QVERIFY(report.open()); QList errors = report.read(); QCOMPARE(errors.size(), 6); diff --git a/gui/xmlreportv2.cpp b/gui/xmlreportv2.cpp index 152f3127e1e..3bb5190e183 100644 --- a/gui/xmlreportv2.cpp +++ b/gui/xmlreportv2.cpp @@ -53,10 +53,12 @@ static const QString IdAttribute = "id"; static const QString SeverityAttribute = "severity"; static const QString MsgAttribute = "msg"; static const QString VersionAttribute = "version"; +static const QString ProductNameAttribute = "product-name"; static const QString VerboseAttribute = "verbose"; -XmlReportV2::XmlReportV2(const QString &filename) : +XmlReportV2::XmlReportV2(const QString &filename, QString productName) : XmlReport(filename), + mProductName(std::move(productName)), mXmlReader(nullptr), mXmlWriter(nullptr) {} @@ -87,12 +89,18 @@ bool XmlReportV2::open() void XmlReportV2::writeHeader() { + const auto nameAndVersion = Settings::getNameAndVersion(mProductName.toStdString()); + const QString name = QString::fromStdString(nameAndVersion.first); + const QString version = nameAndVersion.first.empty() ? CppCheck::version() : QString::fromStdString(nameAndVersion.second); + mXmlWriter->setAutoFormatting(true); mXmlWriter->writeStartDocument(); mXmlWriter->writeStartElement(ResultElementName); mXmlWriter->writeAttribute(VersionAttribute, QString::number(2)); mXmlWriter->writeStartElement(CppcheckElementName); - mXmlWriter->writeAttribute(VersionAttribute, QString(CppCheck::version())); + if (!name.isEmpty()) + mXmlWriter->writeAttribute(ProductNameAttribute, name); + mXmlWriter->writeAttribute(VersionAttribute, version); mXmlWriter->writeEndElement(); mXmlWriter->writeStartElement(ErrorsElementName); } diff --git a/gui/xmlreportv2.h b/gui/xmlreportv2.h index d8ac04f7773..6f93ccdb326 100644 --- a/gui/xmlreportv2.h +++ b/gui/xmlreportv2.h @@ -39,7 +39,7 @@ class QXmlStreamWriter; */ class XmlReportV2 : public XmlReport { public: - explicit XmlReportV2(const QString &filename); + explicit XmlReportV2(const QString &filename, QString productName); ~XmlReportV2() override; /** @@ -82,6 +82,9 @@ class XmlReportV2 : public XmlReport { ErrorItem readError(const QXmlStreamReader *reader); private: + /** Product name read from cppcheck.cfg */ + const QString mProductName; + /** * @brief XML stream reader for reading the report in XML format. */ diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index 8ec05204a62..7da864f41c5 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -425,14 +425,9 @@ void ErrorMessage::deserialize(const std::string &data) std::string ErrorMessage::getXMLHeader(std::string productName) { - std::string version = CppCheck::version(); - if (!productName.empty() && std::isdigit(productName.back())) { - const std::string::size_type pos = productName.find_last_not_of(".0123456789"); - if (pos > 1 && pos != std::string::npos && productName[pos] == ' ') { - version = productName.substr(pos+1); - productName.erase(pos); - } - } + const auto nameAndVersion = Settings::getNameAndVersion(productName); + productName = nameAndVersion.first; + const std::string version = nameAndVersion.first.empty() ? CppCheck::version() : nameAndVersion.second; tinyxml2::XMLPrinter printer; diff --git a/lib/settings.cpp b/lib/settings.cpp index 6ded3497e4e..97a31eaafbc 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -132,6 +132,28 @@ std::string Settings::loadCppcheckCfg() return ""; } +std::pair Settings::getNameAndVersion(const std::string& productName) { + if (productName.empty()) + return {}; + const std::string::size_type pos1 = productName.rfind(' '); + if (pos1 == std::string::npos) + return {}; + if (pos1 + 2 >= productName.length()) + return {}; + for (auto pos2 = pos1 + 1; pos2 < productName.length(); ++pos2) { + const char c = productName[pos2]; + const char prev = productName[pos2-1]; + if (std::isdigit(c)) + continue; + if (c == '.' && std::isdigit(prev)) + continue; + if (c == 's' && pos2 + 1 == productName.length() && std::isdigit(prev)) + continue; + return {}; + } + return {productName.substr(0, pos1), productName.substr(pos1+1)}; +} + std::string Settings::parseEnabled(const std::string &str, std::tuple, SimpleEnableGroup> &groups) { // Enable parameters may be comma separated... diff --git a/lib/settings.h b/lib/settings.h index 81de9a36d89..bb95a4aa5aa 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -101,6 +101,8 @@ class CPPCHECKLIB WARN_UNUSED Settings { std::string loadCppcheckCfg(); + static std::pair getNameAndVersion(const std::string& productName); + /** @brief addons, either filename of python/json file or json data */ std::unordered_set addons; diff --git a/test/testsettings.cpp b/test/testsettings.cpp index 6c2b2de2d7c..a9e8466a2e2 100644 --- a/test/testsettings.cpp +++ b/test/testsettings.cpp @@ -31,6 +31,7 @@ class TestSettings : public TestFixture { TEST_CASE(simpleEnableGroup); TEST_CASE(loadCppcheckCfg); TEST_CASE(loadCppcheckCfgSafety); + TEST_CASE(getNameAndVersion); } void simpleEnableGroup() const { @@ -238,6 +239,21 @@ class TestSettings : public TestFixture { ASSERT_EQUALS(true, s.safety); } } + + void getNameAndVersion() const + { + { + const auto nameVersion = Settings::getNameAndVersion("Cppcheck Premium 12.3.4"); + ASSERT_EQUALS("Cppcheck Premium", nameVersion.first); + ASSERT_EQUALS("12.3.4", nameVersion.second); + } + + { + const auto nameVersion = Settings::getNameAndVersion("Cppcheck Premium 12.3.4s"); + ASSERT_EQUALS("Cppcheck Premium", nameVersion.first); + ASSERT_EQUALS("12.3.4s", nameVersion.second); + } + } }; REGISTER_TEST(TestSettings)