diff --git a/src/base/utils/version.h b/src/base/utils/version.h index b9034c5a5..5dc45189e 100644 --- a/src/base/utils/version.h +++ b/src/base/utils/version.h @@ -29,6 +29,7 @@ #pragma once #include +#include #include #include @@ -42,8 +43,8 @@ namespace Utils template class Version final : public IStringable { - static_assert(N > 0, "The number of version components may not be smaller than 1"); - static_assert(N >= Mandatory, + static_assert((N > 0), "The number of version components may not be smaller than 1"); + static_assert((N >= Mandatory), "The number of mandatory components may not be larger than the total number of components"); public: @@ -52,10 +53,13 @@ namespace Utils constexpr Version() = default; - template + template ...>, int> = 0> constexpr Version(Other ... components) : m_components {{static_cast(components) ...}} { + static_assert((sizeof...(Other) <= N), "Too many parameters provided"); + static_assert((sizeof...(Other) >= Mandatory), "Not enough parameters provided"); } /** @@ -65,7 +69,7 @@ namespace Utils * @throws RuntimeError if parsing fails */ Version(const QString &version) - : Version {version.split(u'.')} + : m_components {parseList(version.split(u'.'))} { } @@ -76,7 +80,7 @@ namespace Utils * @throws RuntimeError if parsing fails */ Version(const QByteArray &version) - : Version {version.split('.')} + : m_components {parseList(version.split('.'))} { } @@ -158,8 +162,8 @@ namespace Utils private: using ComponentsArray = std::array; - template - static ComponentsArray parseList(const StringsList &versionParts) + template + static ComponentsArray parseList(const StringList &versionParts) { if ((static_cast(versionParts.size()) > N) || (static_cast(versionParts.size()) < Mandatory)) @@ -171,19 +175,13 @@ namespace Utils ComponentsArray res {{}}; for (std::size_t i = 0; i < static_cast(versionParts.size()); ++i) { - res[i] = static_cast(versionParts[static_cast(i)].toInt(&ok)); + res[i] = static_cast(versionParts[static_cast(i)].toInt(&ok)); if (!ok) throw RuntimeError(u"Can not parse version component"_qs); } return res; } - template - Version(const StringsList &versionParts) - : m_components (parseList(versionParts)) // GCC 4.8.4 has problems with the uniform initializer here - { - } - ComponentsArray m_components {{}}; }; diff --git a/src/gui/utils.cpp b/src/gui/utils.cpp index a8e8e0f8f..69f27d18e 100644 --- a/src/gui/utils.cpp +++ b/src/gui/utils.cpp @@ -194,7 +194,7 @@ void Utils::Gui::openFolderSelect(const Path &path) proc.waitForFinished(); const auto nautilusVerStr = QString::fromLocal8Bit(proc.readLine()).remove(QRegularExpression(u"[^0-9.]"_qs)); using NautilusVersion = Utils::Version; - if (NautilusVersion::tryParse(nautilusVerStr, {1, 0, 0}) > NautilusVersion {3, 28}) + if (NautilusVersion::tryParse(nautilusVerStr, {1, 0, 0}) > NautilusVersion {3, 28, 0}) proc.startDetached(u"nautilus"_qs, {(Fs::isDir(path) ? path.parentPath() : path).toString()}); else proc.startDetached(u"nautilus"_qs, {u"--no-desktop"_qs, (Fs::isDir(path) ? path.parentPath() : path).toString()}); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3b8babbc8..266e42040 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,11 +15,15 @@ set(testFiles testutilscompare.cpp testutilsgzip.cpp testutilsstring.cpp + testutilsversion.cpp ) + foreach(testFile ${testFiles}) get_filename_component(testFilename "${testFile}" NAME_WLE) add_executable("${testFilename}" "${testFile}") target_link_libraries("${testFilename}" PRIVATE Qt::Test qbt_base) add_test(NAME "${testFilename}" COMMAND "${testFilename}") + + add_dependencies(check "${testFilename}") endforeach() diff --git a/test/testutilsversion.cpp b/test/testutilsversion.cpp new file mode 100644 index 000000000..f83a977bc --- /dev/null +++ b/test/testutilsversion.cpp @@ -0,0 +1,160 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2022 Mike Tzou (Chocobo1) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include + +#include "base/global.h" +#include "base/utils/version.h" + +class TestUtilsVersion final : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(TestUtilsVersion) + +public: + TestUtilsVersion() = default; + +private slots: + void testConstructors() const + { + using TwoDigits = Utils::Version; + TwoDigits(); + TwoDigits(0); + TwoDigits(0, 1); + + using ThreeDigits = Utils::Version; + // should not compile: + // ThreeDigits(1); + // ThreeDigits(1, 2); + // ThreeDigits(1, 2, 3, 4); + QCOMPARE(ThreeDigits(u"1.2.3"_qs), ThreeDigits(1, 2, 3)); + QCOMPARE(ThreeDigits(QByteArrayLiteral("1.2.3")), ThreeDigits(1, 2, 3)); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) + QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(u""_qs)); + QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(u"1"_qs)); + QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(u"1.0"_qs)); + QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(u"1.0.1.1"_qs)); + QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(u"random_string"_qs)); + + QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(QByteArrayLiteral("1"))); + QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(QByteArrayLiteral("1.0"))); + QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(QByteArrayLiteral("1.0.1.1"))); + QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(QByteArrayLiteral("random_string"))); +#endif + } + + void testVersionComponents() const + { + const Utils::Version version1 {1}; + QCOMPARE(version1[0], 1); + QCOMPARE(version1.majorNumber(), 1); + // should not compile: + // version1.minorNumber(); + // version1.revisionNumber(); + // version1.patchNumber(); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) + QVERIFY_THROWS_EXCEPTION(std::out_of_range, version1[1]); + QVERIFY_THROWS_EXCEPTION(std::out_of_range, version1[2]); +#endif + + const Utils::Version version2 {10, 11, 12, 13}; + QCOMPARE(version2[0], 10); + QCOMPARE(version2[1], 11); + QCOMPARE(version2[2], 12); + QCOMPARE(version2[3], 13); + QCOMPARE(version2.majorNumber(), 10); + QCOMPARE(version2.minorNumber(), 11); + QCOMPARE(version2.revisionNumber(), 12); + QCOMPARE(version2.patchNumber(), 13); + } + + void testToString() const + { + using OneMandatory = Utils::Version; + QCOMPARE(OneMandatory(u"10"_qs).toString(), u"10"_qs); + + using FourDigits = Utils::Version; + QCOMPARE(FourDigits().toString(), u"0.0.0.0"_qs); + QCOMPARE(FourDigits(10, 11, 12, 13).toString(), u"10.11.12.13"_qs); + } + + void testIsValid() const + { + using ThreeDigits = Utils::Version; + QCOMPARE(ThreeDigits().isValid(), false); + QCOMPARE(ThreeDigits(10, 11, 12).isValid(), true); + } + + void testTryParse() const + { + using OneMandatory = Utils::Version; + const OneMandatory default1 {10, 11}; + QCOMPARE(OneMandatory::tryParse(u"1"_qs, default1), OneMandatory(1)); + QCOMPARE(OneMandatory::tryParse(u"1.2"_qs, default1), OneMandatory(1, 2)); + QCOMPARE(OneMandatory::tryParse(u"1,2"_qs, default1), default1); + + QCOMPARE(OneMandatory::tryParse(u""_qs, default1), default1); + QCOMPARE(OneMandatory::tryParse(u"random_string"_qs, default1), default1); + + using FourDigits = Utils::Version; + const FourDigits default4 {10, 11, 12, 13}; + QCOMPARE(FourDigits::tryParse(u"1"_qs, default4), default4); + QCOMPARE(FourDigits::tryParse(u"1.2.3.4"_qs, default4), FourDigits(1, 2, 3, 4)); + QCOMPARE(FourDigits::tryParse(u"1,2.3.4"_qs, default4), default4); + } + + void testComparisons() const + { + using ThreeDigits = Utils::Version; + + QVERIFY(ThreeDigits() == ThreeDigits()); + QVERIFY(!(ThreeDigits() != ThreeDigits())); + QVERIFY(ThreeDigits() <= ThreeDigits()); + QVERIFY(ThreeDigits() >= ThreeDigits()); + + QVERIFY(ThreeDigits() != ThreeDigits(1, 2, 3)); + QVERIFY(ThreeDigits() < ThreeDigits(1, 2, 3)); + QVERIFY(ThreeDigits() <= ThreeDigits(1, 2, 3)); + + QVERIFY(ThreeDigits(1, 2, 3) != ThreeDigits()); + QVERIFY(ThreeDigits(1, 2, 3) > ThreeDigits()); + QVERIFY(ThreeDigits(1, 2, 3) >= ThreeDigits()); + + QVERIFY(ThreeDigits(1, 3, 3) != ThreeDigits(2, 2, 3)); + QVERIFY(ThreeDigits(1, 3, 3) < ThreeDigits(2, 2, 3)); + QVERIFY(ThreeDigits(1, 3, 3) <= ThreeDigits(2, 2, 3)); + + QVERIFY(ThreeDigits(1, 2, 3) != ThreeDigits(1, 2, 4)); + QVERIFY(ThreeDigits(1, 2, 3) < ThreeDigits(1, 2, 4)); + QVERIFY(ThreeDigits(1, 2, 3) <= ThreeDigits(1, 2, 4)); + } +}; + +QTEST_APPLESS_MAIN(TestUtilsVersion) +#include "testutilsversion.moc"