File indexing completed on 2024-11-10 04:40:22
0001 /* 0002 SPDX-FileCopyrightText: 2018 Daniel Vrátil <dvratil@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include <QObject> 0008 #include <QTest> 0009 0010 #include "shared/akranges.h" 0011 0012 #include <iostream> 0013 0014 using namespace AkRanges; 0015 0016 namespace 0017 { 0018 int transformFreeFunc(int i) 0019 { 0020 return i * 2; 0021 } 0022 0023 struct TransformHelper { 0024 public: 0025 static int transform(int i) 0026 { 0027 return transformFreeFunc(i); 0028 } 0029 0030 int operator()(int i) const 0031 { 0032 return transformFreeFunc(i); 0033 } 0034 }; 0035 0036 bool filterFreeFunc(int i) 0037 { 0038 return i % 2 == 0; 0039 } 0040 0041 struct FilterHelper { 0042 public: 0043 static bool filter(int i) 0044 { 0045 return filterFreeFunc(i); 0046 } 0047 0048 bool operator()(int i) 0049 { 0050 return filterFreeFunc(i); 0051 } 0052 }; 0053 0054 } // namespace 0055 0056 class AkRangesTest : public QObject 0057 { 0058 Q_OBJECT 0059 0060 private Q_SLOTS: 0061 void testTraits() 0062 { 0063 QVERIFY(AkTraits::isAppendable<QList<int>>); 0064 QVERIFY(!AkTraits::isInsertable<QList<int>>); 0065 QVERIFY(AkTraits::isReservable<QList<int>>); 0066 0067 QVERIFY(!AkTraits::isAppendable<QSet<int>>); 0068 QVERIFY(AkTraits::isInsertable<QSet<int>>); 0069 QVERIFY(AkTraits::isReservable<QSet<int>>); 0070 0071 QVERIFY(!AkTraits::isAppendable<QString>); 0072 QVERIFY(!AkTraits::isInsertable<QString>); 0073 QVERIFY(AkTraits::isReservable<QString>); 0074 } 0075 0076 void testContainerConversion() 0077 { 0078 QHashSeed::setDeterministicGlobalSeed(); 0079 { 0080 QList<int> in = {1, 2, 3, 4, 5}; 0081 QCOMPARE(in | Actions::toQList, in.toList()); 0082 QCOMPARE(in | Actions::toQList | Actions::toQVector, in); 0083 QCOMPARE(in | Actions::toQSet, QSet<int>(in.begin(), in.end())); 0084 } 0085 { 0086 QList<int> in = {1, 2, 3, 4, 5}; 0087 QCOMPARE(in | Actions::toQVector, in.toVector()); 0088 QCOMPARE(in | Actions::toQVector | Actions::toQList, in); 0089 QCOMPARE(in | Actions::toQSet, QSet<int>(in.begin(), in.end())); 0090 } 0091 QHashSeed::resetRandomGlobalSeed(); 0092 } 0093 0094 void testAssociativeContainerConversion() 0095 { 0096 QList<std::pair<int, QString>> in = {{1, QStringLiteral("One")}, {2, QStringLiteral("Two")}, {3, QStringLiteral("Three")}}; 0097 QMap<int, QString> out = {{1, QStringLiteral("One")}, {2, QStringLiteral("Two")}, {3, QStringLiteral("Three")}}; 0098 QCOMPARE(in | Actions::toQMap, out); 0099 } 0100 0101 void testRangeConversion() 0102 { 0103 { 0104 QList<int> in = {1, 2, 3, 4, 5}; 0105 AkRanges::detail::Range<QList<int>::const_iterator> range(in.cbegin(), in.cend()); 0106 QCOMPARE(range | Actions::toQVector, QList<int>::fromList(in)); 0107 } 0108 0109 { 0110 QList<int> in = {1, 2, 3, 4, 5}; 0111 AkRanges::detail::Range<QList<int>::const_iterator> range(in.cbegin(), in.cend()); 0112 QCOMPARE(range | Actions::toQList, in.toList()); 0113 } 0114 0115 { 0116 QList<std::pair<int, QString>> in = {{1, QStringLiteral("One")}, {2, QStringLiteral("Two")}, {3, QStringLiteral("Three")}}; 0117 QMap<int, QString> out = {{1, QStringLiteral("One")}, {2, QStringLiteral("Two")}, {3, QStringLiteral("Three")}}; 0118 AkRanges::detail::Range<QList<std::pair<int, QString>>::const_iterator> range(in.cbegin(), in.cend()); 0119 QCOMPARE(range | Actions::toQMap, out); 0120 } 0121 } 0122 0123 void testTransform() 0124 { 0125 QList<int> in = {1, 2, 3, 4, 5}; 0126 QList<int> out = {2, 4, 6, 8, 10}; 0127 QCOMPARE(in | Views::transform([](int i) { 0128 return i * 2; 0129 }) | Actions::toQList, 0130 out); 0131 QCOMPARE(in | Views::transform(transformFreeFunc) | Actions::toQList, out); 0132 QCOMPARE(in | Views::transform(&TransformHelper::transform) | Actions::toQList, out); 0133 QCOMPARE(in | Views::transform(TransformHelper()) | Actions::toQList, out); 0134 } 0135 0136 private: 0137 class CopyCounter 0138 { 0139 public: 0140 CopyCounter() = default; 0141 CopyCounter(const CopyCounter &other) 0142 : copyCount(other.copyCount + 1) 0143 , transformed(other.transformed) 0144 { 0145 } 0146 CopyCounter(CopyCounter &&other) = default; 0147 CopyCounter &operator=(const CopyCounter &other) 0148 { 0149 copyCount = other.copyCount + 1; 0150 transformed = other.transformed; 0151 return *this; 0152 } 0153 CopyCounter &operator=(CopyCounter &&other) = default; 0154 ~CopyCounter() = default; 0155 0156 int copyCount = 0; 0157 bool transformed = false; 0158 }; 0159 0160 private Q_SLOTS: 0161 0162 void testTransformCopyCount() 0163 { 0164 { 0165 QList<CopyCounter> in = {{}}; // 1st copy (QList::append()) 0166 QList<CopyCounter> out = in | Views::transform([](const auto &c) { 0167 CopyCounter r(c); // 2nd copy (expected) 0168 r.transformed = true; 0169 return r; 0170 }) 0171 | Actions::toQList; // 3rd copy (QList::append()) (Qt5 only) 0172 QCOMPARE(out.size(), in.size()); 0173 QCOMPARE(out[0].copyCount, 2); 0174 QCOMPARE(out[0].transformed, true); 0175 } 0176 0177 { 0178 QList<CopyCounter> in(1); // construct vector of one element, so no copying 0179 // occurs at initialization 0180 QList<CopyCounter> out = in | Views::transform([](const auto &c) { 0181 CopyCounter r(c); // 1st copy 0182 r.transformed = true; 0183 return r; 0184 }) 0185 | Actions::toQVector; 0186 QCOMPARE(out.size(), in.size()); 0187 QCOMPARE(out[0].copyCount, 1); 0188 QCOMPARE(out[0].transformed, true); 0189 } 0190 } 0191 0192 void testTransformConvert() 0193 { 0194 { 0195 QList<int> in = {1, 2, 3, 4, 5}; 0196 QList<int> out = {2, 4, 6, 8, 10}; 0197 QCOMPARE(in | Views::transform([](int i) { 0198 return i * 2; 0199 }) | Actions::toQVector, 0200 out); 0201 } 0202 0203 { 0204 QList<int> in = {1, 2, 3, 4, 5}; 0205 QList<int> out = {2, 4, 6, 8, 10}; 0206 QCOMPARE(in | Views::transform([](int i) { 0207 return i * 2; 0208 }) | Actions::toQList, 0209 out); 0210 } 0211 } 0212 0213 void testCreateRange() 0214 { 0215 { 0216 QList<int> in = {1, 2, 3, 4, 5, 6}; 0217 QList<int> out = {3, 4, 5}; 0218 QCOMPARE(Views::range(in.begin() + 2, in.begin() + 5) | Actions::toQList, out); 0219 } 0220 } 0221 0222 void testRangeWithTransform() 0223 { 0224 { 0225 QList<int> in = {1, 2, 3, 4, 5, 6}; 0226 QList<int> out = {6, 8, 10}; 0227 QCOMPARE(Views::range(in.begin() + 2, in.begin() + 5) | Views::transform([](int i) { 0228 return i * 2; 0229 }) | Actions::toQList, 0230 out); 0231 } 0232 } 0233 0234 void testTransformType() 0235 { 0236 { 0237 QStringList in = {QStringLiteral("foo"), QStringLiteral("foobar"), QStringLiteral("foob")}; 0238 QList<int> out = {3, 6, 4}; 0239 QCOMPARE(in | Views::transform([](const auto &str) { 0240 return (int)str.size(); 0241 }) | Actions::toQList, 0242 out); 0243 } 0244 } 0245 0246 void testFilter() 0247 { 0248 { 0249 QList<int> in = {1, 2, 3, 4, 5, 6, 7, 8}; 0250 QList<int> out = {2, 4, 6, 8}; 0251 QCOMPARE(in | Views::filter([](int i) { 0252 return i % 2 == 0; 0253 }) | Actions::toQList, 0254 out); 0255 QCOMPARE(in | Views::filter(filterFreeFunc) | Actions::toQList, out); 0256 QCOMPARE(in | Views::filter(&FilterHelper::filter) | Actions::toQList, out); 0257 QCOMPARE(in | Views::filter(FilterHelper()) | Actions::toQList, out); 0258 } 0259 } 0260 0261 void testFilterTransform() 0262 { 0263 { 0264 QStringList in = {QStringLiteral("foo"), QStringLiteral("foobar"), QStringLiteral("foob")}; 0265 QList<qsizetype> out = {6}; 0266 QCOMPARE(in | Views::transform(&QString::size) | Views::filter([](int i) { 0267 return i > 5; 0268 }) | Actions::toQList, 0269 out); 0270 QCOMPARE(in | Views::filter([](const auto &str) { 0271 return str.size() > 5; 0272 }) | Views::transform(&QString::size) 0273 | Actions::toQList, 0274 out); 0275 } 0276 } 0277 0278 void testTemporaryContainer() 0279 { 0280 const auto func = [] { 0281 QStringList rv; 0282 for (int i = 0; i < 5; i++) { 0283 rv.push_back(QString::number(i)); 0284 } 0285 return rv; 0286 }; 0287 { 0288 QList<int> out = {0, 2, 4}; 0289 QCOMPARE(func() | Views::transform([](const auto &str) { 0290 return str.toInt(); 0291 }) | Views::filter([](int i) { 0292 return i % 2 == 0; 0293 }) | Actions::toQList, 0294 out); 0295 } 0296 { 0297 QList<int> out = {0, 2, 4}; 0298 QCOMPARE(func() | Views::filter([](const auto &v) { 0299 return v.toInt() % 2 == 0; 0300 }) | Views::transform([](const auto &str) { 0301 return str.toInt(); 0302 }) | Actions::toQList, 0303 out); 0304 } 0305 } 0306 0307 void testTemporaryRange() 0308 { 0309 const auto func = [] { 0310 QStringList rv; 0311 for (int i = 0; i < 5; ++i) { 0312 rv.push_back(QString::number(i)); 0313 } 0314 return rv | Views::transform([](const auto &str) { 0315 return str.toInt(); 0316 }); 0317 }; 0318 QList<int> out = {1, 3}; 0319 QCOMPARE(func() | Views::filter([](int i) { 0320 return i % 2 == 1; 0321 }) | Actions::toQList, 0322 out); 0323 } 0324 0325 private: 0326 struct ForEachCallable { 0327 public: 0328 explicit ForEachCallable(QList<int> &out) 0329 : mOut(out) 0330 { 0331 } 0332 0333 void operator()(int i) 0334 { 0335 mOut.push_back(i); 0336 } 0337 0338 static void append(int i) 0339 { 0340 sOut.push_back(i); 0341 } 0342 static void clear() 0343 { 0344 sOut.clear(); 0345 } 0346 static QList<int> sOut; 0347 0348 private: 0349 QList<int> &mOut; 0350 }; 0351 0352 private Q_SLOTS: 0353 void testForEach() 0354 { 0355 const QList<int> in = {1, 2, 3, 4, 5, 6}; 0356 { 0357 QList<int> out; 0358 in | Actions::forEach([&out](int v) { 0359 out.push_back(v); 0360 }); 0361 QCOMPARE(out, in); 0362 } 0363 { 0364 QList<int> out; 0365 in | Actions::forEach(ForEachCallable(out)); 0366 QCOMPARE(out, in); 0367 } 0368 { 0369 ForEachCallable::clear(); 0370 in | Actions::forEach(&ForEachCallable::append); 0371 QCOMPARE(ForEachCallable::sOut, in); 0372 } 0373 { 0374 QList<int> out; 0375 QCOMPARE(in | Actions::forEach([&out](int v) { 0376 out.push_back(v); 0377 }) | Views::filter([](int v) { 0378 return v % 2 == 0; 0379 }) | Views::transform([](int v) { 0380 return v * 2; 0381 }) | Actions::toQList, 0382 QList<int>({4, 8, 12})); 0383 QCOMPARE(out, in); 0384 } 0385 } 0386 0387 private: 0388 template<template<typename, typename> class Container> 0389 void testKeysValuesHelper() 0390 { 0391 const Container<int, QString> in = {{1, QStringLiteral("1")}, {2, QStringLiteral("2")}, {3, QStringLiteral("3")}}; 0392 0393 QCOMPARE(in | Views::keys | Actions::toQList, in.keys()); 0394 QCOMPARE(in | Views::values | Actions::toQList, in.values()); 0395 } 0396 0397 private Q_SLOTS: 0398 void testKeysValuesQMap() 0399 { 0400 testKeysValuesHelper<QMap>(); 0401 } 0402 0403 void testKeysValuesQHash() 0404 { 0405 testKeysValuesHelper<QHash>(); 0406 } 0407 0408 void testAll() 0409 { 0410 const QList<int> vals = {2, 4, 6, 8, 10}; 0411 QVERIFY(vals | Actions::all([](int v) { 0412 return v % 2 == 0; 0413 })); 0414 QVERIFY(!(vals | Actions::all([](int v) { 0415 return v % 2 == 1; 0416 }))); 0417 } 0418 0419 void testAny() 0420 { 0421 const QList<int> vals = {1, 3, 5, 7, 9}; 0422 QVERIFY(vals | Actions::any([](int v) { 0423 return v % 2 == 1; 0424 })); 0425 QVERIFY(!(vals | Actions::any([](int v) { 0426 return v % 2 == 0; 0427 }))); 0428 } 0429 0430 void testNone() 0431 { 0432 const QList<int> vals = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 0433 QVERIFY(vals | Views::filter([](int i) { 0434 return i % 2 == 0; 0435 }) 0436 | Actions::none([](int i) { 0437 return i % 2 == 1; 0438 })); 0439 QVERIFY(!(vals | Views::filter([](int i) { 0440 return i % 2 == 0; 0441 }) 0442 | Actions::none([](int i) { 0443 return i % 2 == 0; 0444 }))); 0445 } 0446 0447 void testEnumerate() 0448 { 0449 const QList<int> vals = {2, 4, 6, 8, 10}; 0450 for (const auto [idx, val] : vals | Views::enumerate()) { 0451 QCOMPARE(val, (idx + 1) * 2); 0452 } 0453 for (const auto [idx, val] : vals | Views::enumerate(1)) { 0454 QCOMPARE(val, idx * 2); 0455 } 0456 } 0457 }; 0458 0459 QList<int> AkRangesTest::ForEachCallable::sOut; 0460 0461 QTEST_GUILESS_MAIN(AkRangesTest) 0462 0463 #include "akrangestest.moc"