From 0d715d879dd159d0dff33a1e216e94c2acee9cf0 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Mon, 11 Jul 2022 13:55:46 +0800 Subject: [PATCH 1/2] Specify test files manually So that cmake will detect file changes automatically and therefore we don't need to re-invoke cmake manually. --- test/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index de3a36511..2f9a2cf16 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,7 +9,9 @@ add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) include_directories("../src") -file(GLOB testFiles "${CMAKE_CURRENT_SOURCE_DIR}/test*.cpp") +set(testFiles + testutilsgzip.cpp +) foreach(testFile ${testFiles}) get_filename_component(testFilename "${testFile}" NAME_WLE) From 60e62dc5abbcf548cfc42a629711ebe243419c7c Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Sun, 10 Jul 2022 15:21:07 +0800 Subject: [PATCH 2/2] Add testing for various classes --- test/CMakeLists.txt | 3 + test/testalgorithm.cpp | 126 ++++++++++++++++++++++++++ test/testorderedset.cpp | 131 +++++++++++++++++++++++++++ test/testutilscompare.cpp | 184 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 444 insertions(+) create mode 100644 test/testalgorithm.cpp create mode 100644 test/testorderedset.cpp create mode 100644 test/testutilscompare.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2f9a2cf16..5b1e87827 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -10,6 +10,9 @@ add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) include_directories("../src") set(testFiles + testalgorithm.cpp + testorderedset.cpp + testutilscompare.cpp testutilsgzip.cpp ) foreach(testFile ${testFiles}) diff --git a/test/testalgorithm.cpp b/test/testalgorithm.cpp new file mode 100644 index 000000000..8309bfb4e --- /dev/null +++ b/test/testalgorithm.cpp @@ -0,0 +1,126 @@ +/* + * 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 +#include +#include + +#include + +#include "base/algorithm.h" +#include "base/global.h" + +class TestAlgorithm final : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(TestAlgorithm) + +public: + TestAlgorithm() = default; + +private slots: + void testHasMappedType() const + { + QVERIFY(static_cast(Algorithm::HasMappedType>::value)); + QVERIFY(static_cast(Algorithm::HasMappedType>::value)); + QVERIFY(static_cast(Algorithm::HasMappedType>::value)); + QVERIFY(static_cast(Algorithm::HasMappedType>::value)); + + QVERIFY(!static_cast(Algorithm::HasMappedType>::value)); + QVERIFY(!static_cast(Algorithm::HasMappedType>::value)); + QVERIFY(!static_cast(Algorithm::HasMappedType>::value)); + } + + void testMappedTypeRemoveIf() const + { + { + QMap data = + { + {0, 'a'}, + {1, 'b'}, + {2, 'c'}, + {3, 'b'}, + {4, 'd'} + }; + Algorithm::removeIf(data, [](const int key, const char value) + { + Q_UNUSED(key); + return (value == 'b'); + }); + QCOMPARE(data.size(), 3); + QCOMPARE(data.value(0), 'a'); + QVERIFY(!data.contains(1)); + QCOMPARE(data.value(2), 'c'); + QVERIFY(!data.contains(3)); + QCOMPARE(data.value(4), 'd'); + } + { + QHash data; + Algorithm::removeIf(data, [](const int key, const char value) + { + Q_UNUSED(key); + return (value == 'b'); + }); + QVERIFY(data.empty()); + } + } + + void testNonMappedTypeRemoveIf() const + { + { + QSet data = + { + 'a', + 'b', + 'c', + 'b', + 'd' + }; + Algorithm::removeIf(data, [](const char value) + { + return (value == 'b'); + }); + QCOMPARE(data.size(), 3); + QVERIFY(data.contains('a')); + QVERIFY(data.contains('c')); + QVERIFY(data.contains('d')); + } + { + std::set data; + Algorithm::removeIf(data, [](const char value) + { + return (value == 'b'); + }); + QVERIFY(data.empty()); + } + } +}; + +QTEST_APPLESS_MAIN(TestAlgorithm) +#include "testalgorithm.moc" diff --git a/test/testorderedset.cpp b/test/testorderedset.cpp new file mode 100644 index 000000000..a99687b7a --- /dev/null +++ b/test/testorderedset.cpp @@ -0,0 +1,131 @@ +/* + * 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/orderedset.h" + +class TestOrderedSet final : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(TestOrderedSet) + +public: + TestOrderedSet() = default; + +private slots: +#if __cplusplus < 202002L + void testContains() const + { + const OrderedSet set {u"a"_qs, u"b"_qs, u"c"_qs}; + QVERIFY(set.contains(u"a"_qs)); + QVERIFY(set.contains(u"b"_qs)); + QVERIFY(set.contains(u"c"_qs)); + QVERIFY(!set.contains(u"z"_qs)); + + const OrderedSet emptySet; + QVERIFY(!emptySet.contains(u"a"_qs)); + } +#endif + + void testCount() const + { + const OrderedSet set {u"a"_qs, u"b"_qs, u"c"_qs, u"c"_qs}; + QCOMPARE(set.count(), 3); + + const OrderedSet emptySet; + QCOMPARE(emptySet.count(), 0); + } + + void testIntersect() const + { + OrderedSet set {u"a"_qs, u"b"_qs, u"c"_qs}; + set.intersect({u"c"_qs, u"a"_qs}); + QCOMPARE(set.size(), 2); + QCOMPARE(set.join(u","_qs), u"a,c"_qs); + + OrderedSet emptySet; + emptySet.intersect({u"a"_qs}).intersect({u"c"_qs});; + QVERIFY(emptySet.isEmpty()); + } + + void testIsEmpty() const + { + const OrderedSet set {u"a"_qs, u"b"_qs, u"c"_qs}; + QVERIFY(!set.isEmpty()); + + const OrderedSet emptySet; + QVERIFY(emptySet.isEmpty()); + } + + void testJoin() const + { + const OrderedSet set {u"a"_qs, u"b"_qs, u"c"_qs}; + QCOMPARE(set.join(u","_qs), u"a,b,c"_qs); + + const OrderedSet emptySet; + QCOMPARE(emptySet.join(u","_qs), u""_qs); + } + + void testRemove() const + { + OrderedSet set {u"a"_qs, u"b"_qs, u"c"_qs}; + QVERIFY(!set.remove(u"z"_qs)); + QCOMPARE(set.join(u","_qs), u"a,b,c"_qs); + QVERIFY(set.remove(u"b"_qs)); + QCOMPARE(set.join(u","_qs), u"a,c"_qs); + QVERIFY(set.remove(u"a"_qs)); + QCOMPARE(set.join(u","_qs), u"c"_qs); + QVERIFY(set.remove(u"c"_qs)); + QVERIFY(set.isEmpty()); + + OrderedSet emptySet; + QVERIFY(!emptySet.remove(u"a"_qs)); + QVERIFY(emptySet.isEmpty()); + } + + void testUnite() const + { + const OrderedSet newData1 {u"z"_qs}; + const OrderedSet newData2 {u"y"_qs}; + + OrderedSet set {u"a"_qs, u"b"_qs, u"c"_qs}; + set.unite(newData1); + QCOMPARE(set.join(u","_qs), u"a,b,c,z"_qs); + set.unite(newData2); + QCOMPARE(set.join(u","_qs), u"a,b,c,y,z"_qs); + + OrderedSet emptySet; + emptySet.unite(newData1).unite(newData2); + QCOMPARE(emptySet.join(u","_qs), u"y,z"_qs); + } +}; + +QTEST_APPLESS_MAIN(TestOrderedSet) +#include "testorderedset.moc" diff --git a/test/testutilscompare.cpp b/test/testutilscompare.cpp new file mode 100644 index 000000000..0e022c58c --- /dev/null +++ b/test/testutilscompare.cpp @@ -0,0 +1,184 @@ +/* + * 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 + +#include "base/global.h" +#include "base/utils/compare.h" + +#ifndef QBT_USE_QCOLLATOR // only test qbt own implementation, not QCollator +namespace +{ + enum class CompareResult + { + Equal, + Greater, + Less + }; + + struct TestData + { + QString lhs; + QString rhs; + CompareResult caseInsensitiveResult = CompareResult::Equal; + CompareResult caseSensitiveResult = CompareResult::Equal; + }; + + const TestData testData[] = + { + {u""_qs, u""_qs, CompareResult::Equal, CompareResult::Equal}, + {u""_qs, u"a"_qs, CompareResult::Less, CompareResult::Less}, + {u"a"_qs, u""_qs, CompareResult::Greater, CompareResult::Greater}, + + {u"a"_qs, u"a"_qs, CompareResult::Equal, CompareResult::Equal}, + {u"A"_qs, u"a"_qs, CompareResult::Equal, CompareResult::Less}, // ascii code of 'A' is smaller than 'a' + {u"a"_qs, u"A"_qs, CompareResult::Equal, CompareResult::Greater}, + + {u"0"_qs, u"0"_qs, CompareResult::Equal, CompareResult::Equal}, + {u"1"_qs, u"0"_qs, CompareResult::Greater, CompareResult::Greater}, + {u"0"_qs, u"1"_qs, CompareResult::Less, CompareResult::Less}, + + {u"😀"_qs, u"😀"_qs, CompareResult::Equal, CompareResult::Equal}, + {u"😀"_qs, u"😁"_qs, CompareResult::Less, CompareResult::Less}, + {u"😁"_qs, u"😀"_qs, CompareResult::Greater, CompareResult::Greater}, + + {u"a1"_qs, u"a1"_qs, CompareResult::Equal, CompareResult::Equal}, + {u"A1"_qs, u"a1"_qs, CompareResult::Equal, CompareResult::Less}, + {u"a1"_qs, u"A1"_qs, CompareResult::Equal, CompareResult::Greater}, + + {u"a1"_qs, u"a2"_qs, CompareResult::Less, CompareResult::Less}, + {u"A1"_qs, u"a2"_qs, CompareResult::Less, CompareResult::Less}, + {u"a1"_qs, u"A2"_qs, CompareResult::Less, CompareResult::Greater}, + {u"A1"_qs, u"A2"_qs, CompareResult::Less, CompareResult::Less}, + + {u"abc100"_qs, u"abc99"_qs, CompareResult::Greater, CompareResult::Greater}, + {u"ABC100"_qs, u"abc99"_qs, CompareResult::Greater, CompareResult::Less}, + {u"abc100"_qs, u"ABC99"_qs, CompareResult::Greater, CompareResult::Greater}, + {u"ABC100"_qs, u"ABC99"_qs, CompareResult::Greater, CompareResult::Greater}, + + {u"100abc"_qs, u"99abc"_qs, CompareResult::Greater, CompareResult::Greater}, + {u"100ABC"_qs, u"99abc"_qs, CompareResult::Greater, CompareResult::Greater}, + {u"100abc"_qs, u"99ABC"_qs, CompareResult::Greater, CompareResult::Greater}, + {u"100ABC"_qs, u"99ABC"_qs, CompareResult::Greater, CompareResult::Greater}, + + {u"😀😀😀99"_qs, u"😀😀😀100"_qs, CompareResult::Less, CompareResult::Less}, + {u"😀😀😀100"_qs, u"😀😀😀99"_qs, CompareResult::Greater, CompareResult::Greater} + }; + + void testCompare(const TestData &data, const int actual, const CompareResult expected) + { + const auto errorMessage = u"Wrong result. LHS: \"%1\". RHS: \"%2\". Result: %3"_qs + .arg(data.lhs, data.rhs, QString::number(actual)); + + switch (expected) + { + case CompareResult::Equal: + QVERIFY2((actual == 0), qPrintable(errorMessage)); + break; + case CompareResult::Greater: + QVERIFY2((actual > 0), qPrintable(errorMessage)); + break; + case CompareResult::Less: + QVERIFY2((actual < 0), qPrintable(errorMessage)); + break; + default: + QFAIL("Unhandled case"); + break; + } + } + + void testLessThan(const TestData &data, const bool actual, const CompareResult expected) + { + const auto errorMessage = u"Wrong result. LHS: \"%1\". RHS: \"%2\". Result: %3"_qs + .arg(data.lhs, data.rhs, QString::number(actual)); + + switch (expected) + { + case CompareResult::Equal: + case CompareResult::Greater: + QVERIFY2(!actual, qPrintable(errorMessage)); + break; + case CompareResult::Less: + QVERIFY2(actual, qPrintable(errorMessage)); + break; + default: + QFAIL("Unhandled case"); + break; + } + } +} +#endif + +class TestUtilsCompare final : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(TestUtilsCompare) + +public: + TestUtilsCompare() = default; + +#ifndef QBT_USE_QCOLLATOR // only test qbt own implementation, not QCollator +private slots: + void testNaturalCompareCaseInsensitive() const + { + const Utils::Compare::NaturalCompare cmp; + + for (const TestData &data : testData) + testCompare(data, cmp(data.lhs, data.rhs), data.caseInsensitiveResult); + } + + void testNaturalCompareCaseSensitive() const + { + const Utils::Compare::NaturalCompare cmp; + + for (const TestData &data : testData) + testCompare(data, cmp(data.lhs, data.rhs), data.caseSensitiveResult); + } + + void testNaturalLessThanCaseInsensitive() const + { + const Utils::Compare::NaturalLessThan cmp {}; + + for (const TestData &data : testData) + testLessThan(data, cmp(data.lhs, data.rhs), data.caseInsensitiveResult); + } + + void testNaturalLessThanCaseSensitive() const + { + const Utils::Compare::NaturalLessThan cmp {}; + + for (const TestData &data : testData) + testLessThan(data, cmp(data.lhs, data.rhs), data.caseSensitiveResult); + } +#endif +}; + +QTEST_APPLESS_MAIN(TestUtilsCompare) +#include "testutilscompare.moc"