File indexing completed on 2024-11-17 03:42:09
0001 /* 0002 This file is part of the KTextTemplate library 0003 0004 SPDX-FileCopyrightText: 2010 Michael Jansen <kde@michael-jansen.biz> 0005 SPDX-FileCopyrightText: 2010 Stephen Kelly <steveire@gmail.com> 0006 0007 SPDX-License-Identifier: LGPL-2.1-or-later 0008 0009 */ 0010 0011 #include "engine.h" 0012 #include "ktexttemplate_paths.h" 0013 #include "metatype.h" 0014 #include "template.h" 0015 #include "test_macros.h" 0016 0017 #include <QMetaType> 0018 #include <QQueue> 0019 #include <QStack> 0020 #include <QTest> 0021 #include <QVariant> 0022 #include <QVariantHash> 0023 0024 #include <deque> 0025 #include <string> 0026 0027 #include <memory> 0028 0029 Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(ThreeArray) 0030 0031 Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(QtUnorderedMap) 0032 0033 Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr) 0034 0035 Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(std::deque) 0036 0037 class TestGenericTypes : public QObject 0038 { 0039 Q_OBJECT 0040 0041 private Q_SLOTS: 0042 0043 void initTestCase(); 0044 0045 void testGenericClassType(); 0046 void testSequentialContainer_Variant(); 0047 void testAssociativeContainer_Variant(); 0048 void testSequentialContainer_Type(); 0049 void testAssociativeContainer_Type(); 0050 void testSharedPointer(); 0051 void testThirdPartySharedPointer(); 0052 void testNestedContainers(); 0053 0054 void testCustomQObjectDerived(); 0055 0056 void propertyMacroTypes(); 0057 0058 void testUnregistered(); 0059 void testPointerNonQObject(); 0060 void testQGadget(); 0061 void testGadgetMetaType(); 0062 0063 }; // class TestGenericTypes 0064 0065 class Person 0066 { 0067 public: 0068 Person() 0069 : age(0) 0070 { 0071 } 0072 Person(std::string _name, int _age) 0073 : name(_name) 0074 , age(_age) 0075 { 0076 static auto _uid = 0; 0077 uid = ++_uid; 0078 } 0079 0080 bool operator==(const Person &other) const 0081 { 0082 return uid == other.uid; 0083 } 0084 0085 std::string name; 0086 int age; 0087 int uid; 0088 }; 0089 0090 class PersonGadget 0091 { 0092 Q_GADGET 0093 Q_PROPERTY(QString name MEMBER m_name) 0094 public: 0095 QString m_name; 0096 int m_age = 42; 0097 }; 0098 0099 int qHash(const Person &p) 0100 { 0101 return p.uid; 0102 } 0103 0104 Q_DECLARE_METATYPE(Person) 0105 Q_DECLARE_METATYPE(PersonGadget) 0106 0107 KTEXTTEMPLATE_BEGIN_LOOKUP(Person) 0108 if (property == QStringLiteral("name")) 0109 return QString::fromStdString(object.name); 0110 if (property == QStringLiteral("age")) 0111 return object.age; 0112 KTEXTTEMPLATE_END_LOOKUP 0113 0114 KTEXTTEMPLATE_BEGIN_LOOKUP(PersonGadget) 0115 if (property == QStringLiteral("age")) 0116 return object.m_age; 0117 KTEXTTEMPLATE_END_LOOKUP 0118 0119 class PersonObject : public QObject 0120 { 0121 Q_OBJECT 0122 Q_PROPERTY(QString name READ name) 0123 Q_PROPERTY(int age READ age) 0124 public: 0125 PersonObject(const QString &name, int age, QObject *parent = {}) 0126 : QObject(parent) 0127 , m_name(name) 0128 , m_age(age) 0129 { 0130 } 0131 0132 QString name() const 0133 { 0134 return m_name; 0135 } 0136 int age() const 0137 { 0138 return m_age; 0139 } 0140 0141 private: 0142 const QString m_name; 0143 const int m_age; // Yeah, you wish... 0144 }; 0145 0146 void TestGenericTypes::initTestCase() 0147 { 0148 // Register the handler for our custom type 0149 KTextTemplate::registerMetaType<Person>(); 0150 KTextTemplate::registerMetaType<PersonGadget>(); 0151 } 0152 0153 void TestGenericTypes::testGenericClassType() 0154 { 0155 KTextTemplate::Engine engine; 0156 0157 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0158 0159 auto t1 = engine.newTemplate(QStringLiteral("Person: \nName: {{p.name}}\nAge: {{p.age}}\nUnknown: {{p.unknown}}"), QStringLiteral("template1")); 0160 0161 // Check it 0162 QVariantHash h; 0163 Person p("Grant Lee", 2); 0164 h.insert(QStringLiteral("p"), QVariant::fromValue(p)); 0165 KTextTemplate::Context c(h); 0166 QCOMPARE(t1->render(&c), QStringLiteral("Person: \nName: Grant Lee\nAge: 2\nUnknown: ")); 0167 } 0168 0169 static QMap<int, Person> getPeople() 0170 { 0171 QMap<int, Person> people; 0172 people.insert(23, Person("Claire", 23)); 0173 people.insert(32, Person("Grant", 32)); 0174 people.insert(50, Person("Alan", 50)); 0175 return people; 0176 } 0177 0178 template<typename SequentialContainer> 0179 void insertPeopleVariants(KTextTemplate::Context &c) 0180 { 0181 auto people = getPeople(); 0182 auto it = people.constBegin(); 0183 const auto end = people.constEnd(); 0184 SequentialContainer container; 0185 for (; it != end; ++it) 0186 container.push_back(QVariant::fromValue(it.value())); 0187 c.insert(QStringLiteral("people"), QVariant::fromValue(container)); 0188 } 0189 0190 template<typename AssociativeContainer> 0191 void insertAssociatedPeopleVariants(KTextTemplate::Context &c) 0192 { 0193 auto people = getPeople(); 0194 auto it = people.constBegin(); 0195 const auto end = people.constEnd(); 0196 AssociativeContainer container; 0197 for (; it != end; ++it) 0198 container.insert(QString::number(it.key()), QVariant::fromValue(it.value())); 0199 c.insert(QStringLiteral("people"), QVariant::fromValue(container)); 0200 } 0201 0202 template<> 0203 void insertPeopleVariants<QMap<QString, QVariant>>(KTextTemplate::Context &c) 0204 { 0205 insertAssociatedPeopleVariants<QMap<QString, QVariant>>(c); 0206 } 0207 0208 template<> 0209 void insertPeopleVariants<QHash<QString, QVariant>>(KTextTemplate::Context &c) 0210 { 0211 insertAssociatedPeopleVariants<QHash<QString, QVariant>>(c); 0212 } 0213 0214 template<typename Container> 0215 void testSequentialIteration(KTextTemplate::Context &c) 0216 { 0217 KTextTemplate::Engine engine; 0218 0219 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0220 0221 { 0222 KTextTemplate::Template t1 = 0223 engine.newTemplate(QStringLiteral("{% for person in people %}{{ person.name }},{% endfor %}"), QStringLiteral("people_template")); 0224 QCOMPARE(t1->render(&c), QStringLiteral("Claire,Grant,Alan,")); 0225 } 0226 } 0227 0228 template<typename Container> 0229 void testSequentialIndexing(KTextTemplate::Context &c) 0230 { 0231 KTextTemplate::Engine engine; 0232 0233 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0234 0235 { 0236 KTextTemplate::Template t1 = 0237 engine.newTemplate(QStringLiteral("{{ people.0.name }},{{ people.1.name }},{{ people.2.name }},"), QStringLiteral("people_template")); 0238 QCOMPARE(t1->render(&c), QStringLiteral("Claire,Grant,Alan,")); 0239 } 0240 } 0241 0242 template<typename Container> 0243 struct SequentialContainerTester { 0244 static void iteration(KTextTemplate::Context &c) 0245 { 0246 testSequentialIteration<Container>(c); 0247 } 0248 0249 static void indexing(KTextTemplate::Context &c) 0250 { 0251 testSequentialIndexing<Container>(c); 0252 } 0253 }; 0254 0255 template<typename T> 0256 struct SequentialContainerTester<QSet<T>> { 0257 static void iteration(KTextTemplate::Context &c) 0258 { 0259 KTextTemplate::Engine engine; 0260 0261 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0262 0263 KTextTemplate::Template t1 = 0264 engine.newTemplate(QStringLiteral("{% for person in people %}{{ person.name }},{% endfor %}"), QStringLiteral("people_template")); 0265 auto result = t1->render(&c); 0266 QStringList output{QStringLiteral("Claire,"), QStringLiteral("Grant,"), QStringLiteral("Alan,")}; 0267 for (const QString &s : output) { 0268 QVERIFY(result.contains(s)); 0269 } 0270 0271 QCOMPARE(result.length(), output.join(QString()).length()); 0272 } 0273 0274 static void indexing(KTextTemplate::Context) 0275 { 0276 } 0277 }; 0278 0279 template<typename T> 0280 struct SequentialContainerTester<std::list<T>> { 0281 static void iteration(KTextTemplate::Context &c) 0282 { 0283 testSequentialIteration<std::list<T>>(c); 0284 } 0285 0286 static void indexing(KTextTemplate::Context) 0287 { 0288 } 0289 }; 0290 0291 template<typename Container> 0292 void doTestSequentialContainer_Variant() 0293 { 0294 KTextTemplate::Context c; 0295 0296 insertPeopleVariants<Container>(c); 0297 0298 SequentialContainerTester<Container>::iteration(c); 0299 SequentialContainerTester<Container>::indexing(c); 0300 } 0301 0302 template<typename Container> 0303 void testAssociativeValues(KTextTemplate::Context &c, bool unordered = {}) 0304 { 0305 KTextTemplate::Engine engine; 0306 0307 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0308 0309 { 0310 KTextTemplate::Template t1 = engine.newTemplate(QStringLiteral("{% for person in people.values %}({{ person.name }}:{{ " 0311 "person.age }}),{% endfor %}"), 0312 QStringLiteral("people_template")); 0313 0314 auto result = t1->render(&c); 0315 if (!unordered) 0316 QCOMPARE(result, QStringLiteral("(Claire:23),(Grant:32),(Alan:50),")); 0317 else { 0318 QCOMPARE(result.size(), 33); 0319 QVERIFY(result.contains(QStringLiteral("(Claire:23),"))); 0320 QVERIFY(result.contains(QStringLiteral("(Grant:32),"))); 0321 QVERIFY(result.contains(QStringLiteral("(Alan:50),"))); 0322 } 0323 } 0324 } 0325 0326 template<typename Container> 0327 void testAssociativeItems(KTextTemplate::Context &c, bool unordered) 0328 { 0329 KTextTemplate::Engine engine; 0330 0331 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0332 0333 { 0334 KTextTemplate::Template t1 = engine.newTemplate(QStringLiteral("{% for item in people.items %}({{ item.1.name }}:{{ " 0335 "item.1.age }}),{% endfor %}"), 0336 QStringLiteral("people_template")); 0337 auto result = t1->render(&c); 0338 if (!unordered) 0339 QCOMPARE(result, QStringLiteral("(Claire:23),(Grant:32),(Alan:50),")); 0340 else { 0341 QCOMPARE(result.size(), 33); 0342 QVERIFY(result.contains(QStringLiteral("(Claire:23),"))); 0343 QVERIFY(result.contains(QStringLiteral("(Grant:32),"))); 0344 QVERIFY(result.contains(QStringLiteral("(Alan:50),"))); 0345 } 0346 } 0347 } 0348 0349 template<typename Container> 0350 void doTestAssociativeContainer_Variant(bool unordered = {}) 0351 { 0352 KTextTemplate::Engine engine; 0353 0354 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0355 0356 KTextTemplate::Context c; 0357 0358 insertPeopleVariants<Container>(c); 0359 testAssociativeValues<Container>(c, unordered); 0360 testAssociativeItems<Container>(c, unordered); 0361 } 0362 0363 void TestGenericTypes::testSequentialContainer_Variant() 0364 { 0365 doTestSequentialContainer_Variant<QVariantList>(); 0366 doTestSequentialContainer_Variant<QList<QVariant>>(); 0367 doTestSequentialContainer_Variant<QStack<QVariant>>(); 0368 doTestSequentialContainer_Variant<QQueue<QVariant>>(); 0369 } 0370 0371 void TestGenericTypes::testAssociativeContainer_Variant() 0372 { 0373 doTestAssociativeContainer_Variant<QVariantMap>(); 0374 doTestAssociativeContainer_Variant<QVariantHash>(true); 0375 } 0376 0377 template<typename SequentialContainer> 0378 void insertPeople(KTextTemplate::Context &c) 0379 { 0380 auto people = getPeople(); 0381 auto it = people.constBegin(); 0382 const auto end = people.constEnd(); 0383 SequentialContainer container; 0384 for (; it != end; ++it) 0385 container.insert(container.end(), it.value()); 0386 c.insert(QStringLiteral("people"), QVariant::fromValue(container)); 0387 } 0388 0389 template<> 0390 void insertPeople<QSet<Person>>(KTextTemplate::Context &c) 0391 { 0392 auto people = getPeople(); 0393 auto it = people.constBegin(); 0394 const auto end = people.constEnd(); 0395 QSet<Person> container; 0396 for (; it != end; ++it) 0397 container.insert(it.value()); 0398 c.insert(QStringLiteral("people"), QVariant::fromValue(container)); 0399 } 0400 0401 template<> 0402 void insertPeople<ThreeArray<Person>>(KTextTemplate::Context &c) 0403 { 0404 auto people = getPeople(); 0405 auto it = people.constBegin(); 0406 ThreeArray<Person> container; 0407 for (auto i = 0; i < 3; ++i, ++it) { 0408 Q_ASSERT(it != people.constEnd()); 0409 container[i] = it.value(); 0410 } 0411 c.insert(QStringLiteral("people"), QVariant::fromValue(container)); 0412 } 0413 0414 template<typename AssociativeContainer> 0415 void insertAssociatedPeople(KTextTemplate::Context &c) 0416 { 0417 auto people = getPeople(); 0418 auto it = people.constBegin(); 0419 const auto end = people.constEnd(); 0420 AssociativeContainer container; 0421 for (; it != end; ++it) 0422 container[QString::number(it.key())] = it.value(); 0423 c.insert(QStringLiteral("people"), QVariant::fromValue(container)); 0424 } 0425 0426 template<typename AssociativeContainer> 0427 void insertAssociatedPeople_Number(KTextTemplate::Context &c) 0428 { 0429 auto people = getPeople(); 0430 auto it = people.constBegin(); 0431 const auto end = people.constEnd(); 0432 AssociativeContainer container; 0433 for (; it != end; ++it) 0434 container[it.key()] = it.value(); 0435 c.insert(QStringLiteral("people"), QVariant::fromValue(container)); 0436 } 0437 0438 template<typename Container> 0439 void doTestSequentialContainer_Type() 0440 { 0441 KTextTemplate::Context c; 0442 0443 insertPeople<Container>(c); 0444 0445 SequentialContainerTester<Container>::iteration(c); 0446 SequentialContainerTester<Container>::indexing(c); 0447 } 0448 0449 template<typename Container> 0450 void doTestAssociativeContainer_Type(bool unordered = {}) 0451 { 0452 KTextTemplate::Engine engine; 0453 0454 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0455 0456 KTextTemplate::Context c; 0457 0458 insertAssociatedPeople<Container>(c); 0459 testAssociativeValues<Container>(c, unordered); 0460 testAssociativeItems<Container>(c, unordered); 0461 } 0462 0463 template<typename Container> 0464 void doTestAssociativeContainer_Type_Number(bool unordered = {}) 0465 { 0466 KTextTemplate::Engine engine; 0467 0468 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0469 0470 KTextTemplate::Context c; 0471 0472 insertAssociatedPeople_Number<Container>(c); 0473 testAssociativeValues<Container>(c, unordered); 0474 testAssociativeItems<Container>(c, unordered); 0475 0476 { 0477 KTextTemplate::Template t1 = engine.newTemplate(QStringLiteral("{{ people.23.name }}"), QStringLiteral("claire_template")); 0478 auto result = t1->render(&c); 0479 QCOMPARE(result, QStringLiteral("Claire")); 0480 } 0481 } 0482 0483 void TestGenericTypes::testSequentialContainer_Type() 0484 { 0485 doTestSequentialContainer_Type<QList<Person>>(); 0486 doTestSequentialContainer_Type<QList<Person>>(); 0487 doTestSequentialContainer_Type<QStack<Person>>(); 0488 doTestSequentialContainer_Type<QQueue<Person>>(); 0489 doTestSequentialContainer_Type<QSet<Person>>(); 0490 doTestSequentialContainer_Type<std::deque<Person>>(); 0491 doTestSequentialContainer_Type<std::vector<Person>>(); 0492 doTestSequentialContainer_Type<std::list<Person>>(); 0493 doTestSequentialContainer_Type<ThreeArray<Person>>(); 0494 } 0495 0496 void TestGenericTypes::testAssociativeContainer_Type() 0497 { 0498 doTestAssociativeContainer_Type<QMap<QString, Person>>(); 0499 doTestAssociativeContainer_Type_Number<QMap<qint16, Person>>(); 0500 doTestAssociativeContainer_Type_Number<QMap<qint32, Person>>(); 0501 doTestAssociativeContainer_Type_Number<QMap<qint64, Person>>(); 0502 doTestAssociativeContainer_Type_Number<QMap<quint16, Person>>(); 0503 doTestAssociativeContainer_Type_Number<QMap<quint32, Person>>(); 0504 doTestAssociativeContainer_Type_Number<QMap<quint64, Person>>(); 0505 doTestAssociativeContainer_Type<QHash<QString, Person>>(true); 0506 doTestAssociativeContainer_Type_Number<QHash<qint16, Person>>(true); 0507 doTestAssociativeContainer_Type_Number<QHash<qint32, Person>>(true); 0508 doTestAssociativeContainer_Type_Number<QHash<qint64, Person>>(true); 0509 doTestAssociativeContainer_Type_Number<QHash<quint16, Person>>(true); 0510 doTestAssociativeContainer_Type_Number<QHash<quint32, Person>>(true); 0511 doTestAssociativeContainer_Type_Number<QHash<quint64, Person>>(true); 0512 0513 doTestAssociativeContainer_Type<std::map<QString, Person>>(); 0514 doTestAssociativeContainer_Type_Number<std::map<qint16, Person>>(); 0515 doTestAssociativeContainer_Type_Number<std::map<qint32, Person>>(); 0516 doTestAssociativeContainer_Type_Number<std::map<qint64, Person>>(); 0517 doTestAssociativeContainer_Type_Number<std::map<quint16, Person>>(); 0518 doTestAssociativeContainer_Type_Number<std::map<quint32, Person>>(); 0519 doTestAssociativeContainer_Type_Number<std::map<quint64, Person>>(); 0520 0521 doTestAssociativeContainer_Type<QtUnorderedMap<QString, Person>>(true); 0522 doTestAssociativeContainer_Type_Number<QtUnorderedMap<qint16, Person>>(true); 0523 doTestAssociativeContainer_Type_Number<QtUnorderedMap<qint32, Person>>(true); 0524 doTestAssociativeContainer_Type_Number<QtUnorderedMap<qint64, Person>>(true); 0525 doTestAssociativeContainer_Type_Number<QtUnorderedMap<quint16, Person>>(true); 0526 doTestAssociativeContainer_Type_Number<QtUnorderedMap<quint32, Person>>(true); 0527 doTestAssociativeContainer_Type_Number<QtUnorderedMap<quint64, Person>>(true); 0528 } 0529 0530 void TestGenericTypes::testSharedPointer() 0531 { 0532 KTextTemplate::Engine engine; 0533 0534 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0535 0536 auto t1 = engine.newTemplate(QStringLiteral("{{ p.name }} {{ p.age }}"), QStringLiteral("template1")); 0537 0538 // Check it 0539 QVariantHash h; 0540 QSharedPointer<PersonObject> p(new PersonObject(QStringLiteral("Grant Lee"), 2)); 0541 h.insert(QStringLiteral("p"), QVariant::fromValue(p)); 0542 KTextTemplate::Context c(h); 0543 QCOMPARE(t1->render(&c), QStringLiteral("Grant Lee 2")); 0544 } 0545 0546 void TestGenericTypes::testThirdPartySharedPointer() 0547 { 0548 KTextTemplate::Engine engine; 0549 0550 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0551 0552 auto t1 = engine.newTemplate(QStringLiteral("{{ p.name }} {{ p.age }}"), QStringLiteral("template1")); 0553 0554 // Check it 0555 QVariantHash h; 0556 std::shared_ptr<PersonObject> p(new PersonObject(QStringLiteral("Grant Lee"), 2)); 0557 h.insert(QStringLiteral("p"), QVariant::fromValue(p)); 0558 KTextTemplate::Context c(h); 0559 QCOMPARE(t1->render(&c), QStringLiteral("Grant Lee 2")); 0560 } 0561 0562 using ListVectorInt = QList<QList<qint16>>; 0563 using MapListVectorInt = QMap<int, QList<QList<qint16>>>; 0564 using StackMapListVectorInt = QStack<QMap<int, QList<QList<qint16>>>>; 0565 0566 static QList<qint16> getNumbers() 0567 { 0568 static auto n = 0; 0569 QList<qint16> nums; 0570 nums.push_back(++n); 0571 nums.push_back(++n); 0572 return nums; 0573 } 0574 0575 static ListVectorInt getNumberLists() 0576 { 0577 ListVectorInt list; 0578 for (auto i = 0; i < 2; ++i) { 0579 list.append(getNumbers()); 0580 } 0581 return list; 0582 } 0583 0584 static MapListVectorInt getNumberListMap() 0585 { 0586 MapListVectorInt map; 0587 for (auto i = 0; i < 2; ++i) { 0588 map.insert(i, getNumberLists()); 0589 } 0590 return map; 0591 } 0592 0593 static StackMapListVectorInt getMapStack() 0594 { 0595 StackMapListVectorInt stack; 0596 for (auto i = 0; i < 2; ++i) { 0597 stack.push(getNumberListMap()); 0598 } 0599 return stack; 0600 } 0601 0602 void TestGenericTypes::testNestedContainers() 0603 { 0604 KTextTemplate::Engine engine; 0605 0606 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0607 0608 KTextTemplate::Context c; 0609 c.insert(QStringLiteral("stack"), QVariant::fromValue(getMapStack())); 0610 0611 #if defined(Q_CC_MSVC) 0612 // MSVC doesn't like static string concatenations like L"foo" "bar", as 0613 // results from QStringLiteral, so use QLatin1String here instead. 0614 #define STRING_LITERAL QLatin1String 0615 #else 0616 #define STRING_LITERAL QStringLiteral 0617 #endif 0618 auto t1 = engine.newTemplate(STRING_LITERAL("{% for map in stack %}" 0619 "(M {% for key, list in map.items %}" 0620 "({{ key }} : (L {% for vector in list %}" 0621 "(V {% for number in vector %}" 0622 "{{ number }}," 0623 "{% endfor %})," 0624 "{% endfor %})," 0625 "{% endfor %})," 0626 "{% endfor %}"), 0627 QStringLiteral("template1")); 0628 0629 #undef STRING_LITERAL 0630 0631 auto result = t1->render(&c); 0632 0633 auto expectedResult = QStringLiteral( 0634 "(M (0 : (L (V 1,2,),(V 3,4,),),(1 : (L (V 5,6,),(V 7,8,),),),(M (0 : (L " 0635 "(V 9,10,),(V 11,12,),),(1 : (L (V 13,14,),(V 15,16,),),),"); 0636 0637 QCOMPARE(result, expectedResult); 0638 } 0639 0640 class CustomObject : public QObject 0641 { 0642 Q_OBJECT 0643 public: 0644 explicit CustomObject(QObject *parent = {}) 0645 : QObject(parent) 0646 { 0647 } 0648 }; 0649 0650 class OtherObject : public QObject 0651 { 0652 Q_OBJECT 0653 Q_PROPERTY(CustomObject *custom READ custom) 0654 public: 0655 explicit OtherObject(QObject *parent = {}) 0656 : QObject(parent) 0657 , m_custom(new CustomObject(this)) 0658 { 0659 m_custom->setProperty("nestedProp", QStringLiteral("nestedValue")); 0660 } 0661 0662 CustomObject *custom() 0663 { 0664 return m_custom; 0665 } 0666 0667 private: 0668 CustomObject *m_custom; 0669 }; 0670 0671 void TestGenericTypes::testCustomQObjectDerived() 0672 { 0673 KTextTemplate::Engine engine; 0674 0675 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0676 0677 auto customObject = new CustomObject(this); 0678 customObject->setProperty("someProp", QStringLiteral("propValue")); 0679 0680 KTextTemplate::Context c; 0681 c.insert(QStringLiteral("custom"), QVariant::fromValue(customObject)); 0682 0683 { 0684 auto t1 = engine.newTemplate(QStringLiteral("{{ custom.someProp }}"), QStringLiteral("template1")); 0685 0686 auto result = t1->render(&c); 0687 auto expectedResult = QStringLiteral("propValue"); 0688 0689 QCOMPARE(result, expectedResult); 0690 } 0691 0692 auto other = new OtherObject(this); 0693 0694 c.insert(QStringLiteral("other"), other); 0695 0696 { 0697 auto t1 = engine.newTemplate(QStringLiteral("{{ other.custom.nestedProp }}"), QStringLiteral("template1")); 0698 0699 auto result = t1->render(&c); 0700 auto expectedResult = QStringLiteral("nestedValue"); 0701 0702 QCOMPARE(result, expectedResult); 0703 } 0704 } 0705 0706 struct UnregisteredType { 0707 }; 0708 0709 Q_DECLARE_METATYPE(UnregisteredType) 0710 0711 struct RegisteredNotListType { 0712 }; 0713 0714 Q_DECLARE_METATYPE(RegisteredNotListType) 0715 0716 KTEXTTEMPLATE_BEGIN_LOOKUP(RegisteredNotListType) 0717 Q_UNUSED(object) 0718 if (property == QStringLiteral("property")) 0719 return 42; 0720 KTEXTTEMPLATE_END_LOOKUP 0721 0722 static QVariantList dummy(const UnregisteredType &) 0723 { 0724 return QVariantList{42}; 0725 } 0726 0727 QVariant dummyLookup(const QVariant &, const QString &) 0728 { 0729 return 42; 0730 } 0731 0732 void TestGenericTypes::testUnregistered() 0733 { 0734 { 0735 UnregisteredType unregType; 0736 auto v = QVariant::fromValue(unregType); 0737 0738 auto result = KTextTemplate::MetaType::lookup(v, QStringLiteral("property")); 0739 QVERIFY(!result.isValid()); 0740 0741 QVERIFY(!v.canConvert<QVariantList>()); 0742 } 0743 0744 KTextTemplate::registerMetaType<RegisteredNotListType>(); 0745 0746 { 0747 RegisteredNotListType nonListType; 0748 auto v = QVariant::fromValue(nonListType); 0749 auto result = KTextTemplate::MetaType::lookup(v, QStringLiteral("property")); 0750 QVERIFY(result.isValid()); 0751 QVERIFY(!v.canConvert<QVariantList>()); 0752 } 0753 0754 { 0755 QMetaType::registerConverter<UnregisteredType, QVariantList>(&dummy); 0756 UnregisteredType unregType; 0757 auto v = QVariant::fromValue(unregType); 0758 auto result = KTextTemplate::MetaType::lookup(v, QStringLiteral("property")); 0759 QVERIFY(!result.isValid()); 0760 } 0761 0762 // Only do this in release mode? 0763 // KTextTemplate::MetaType::registerLookUpOperator(0, dummyLookup); 0764 // KTextTemplate::MetaType::registerToVariantListOperator(0, dummy); 0765 } 0766 0767 Q_DECLARE_METATYPE(Person *) 0768 0769 KTEXTTEMPLATE_BEGIN_LOOKUP_PTR(Person) 0770 if (property == QStringLiteral("name")) 0771 return QString::fromStdString(object->name); 0772 if (property == QStringLiteral("age")) 0773 return object->age; 0774 KTEXTTEMPLATE_END_LOOKUP 0775 0776 void TestGenericTypes::testPointerNonQObject() 0777 { 0778 auto p = new Person("Adele", 21); 0779 auto v = QVariant::fromValue(p); 0780 0781 KTextTemplate::registerMetaType<Person *>(); 0782 0783 auto result = KTextTemplate::MetaType::lookup(v, QStringLiteral("name")); 0784 0785 QCOMPARE(result.value<QString>(), QStringLiteral("Adele")); 0786 0787 delete p; 0788 } 0789 0790 class CustomGadget 0791 { 0792 Q_GADGET 0793 Q_PROPERTY(int fortyTwo READ fortyTwo) 0794 public: 0795 int fortyTwo() 0796 { 0797 return 42; 0798 } 0799 }; 0800 0801 void TestGenericTypes::testQGadget() 0802 { 0803 CustomGadget g; 0804 auto v = QVariant::fromValue(g); 0805 0806 auto result = KTextTemplate::MetaType::lookup(v, QStringLiteral("fortyTwo")); 0807 0808 QCOMPARE(result.value<int>(), 42); 0809 } 0810 0811 void TestGenericTypes::testGadgetMetaType() 0812 { 0813 KTextTemplate::Engine engine; 0814 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0815 0816 auto t1 = engine.newTemplate(QStringLiteral("Person: \nName: {{p.name}}\nAge: {{p.age}}"), QStringLiteral("template1")); 0817 0818 PersonGadget p; 0819 p.m_name = QStringLiteral("Some Name"); 0820 KTextTemplate::Context c; 0821 c.insert(QStringLiteral("p"), QVariant::fromValue(p)); 0822 QCOMPARE(t1->render(&c), QStringLiteral("Person: \nName: Some Name\nAge: 42")); 0823 } 0824 0825 class ObjectWithProperties : public QObject 0826 { 0827 Q_OBJECT 0828 Q_PROPERTY(QList<int> numberList READ numberList) 0829 Q_PROPERTY(QList<CustomGadget> gadgetList READ gadgetList) 0830 Q_PROPERTY(QList<PersonObject *> personList READ personList) 0831 Q_PROPERTY(QList<QSharedPointer<PersonObject>> personPtrList READ personPtrList) 0832 0833 public: 0834 ObjectWithProperties(QObject *parent = {}) 0835 : QObject(parent) 0836 { 0837 m_numberList.push_back(42); 0838 m_numberList.push_back(7); 0839 m_gadgetList.push_back(CustomGadget{}); 0840 m_gadgetList.push_back(CustomGadget{}); 0841 m_personList.push_back(new PersonObject{QStringLiteral("Joe"), 20, this}); 0842 m_personList.push_back(new PersonObject{QStringLiteral("Mike"), 22, this}); 0843 m_personPtrList.push_back(QSharedPointer<PersonObject>::create(QStringLiteral("Niall"), 23)); 0844 m_personPtrList.push_back(QSharedPointer<PersonObject>::create(QStringLiteral("Dave"), 24)); 0845 } 0846 0847 QList<int> numberList() 0848 { 0849 return m_numberList; 0850 } 0851 QList<CustomGadget> gadgetList() 0852 { 0853 return m_gadgetList; 0854 } 0855 QList<PersonObject *> personList() 0856 { 0857 return m_personList; 0858 } 0859 QList<QSharedPointer<PersonObject>> personPtrList() 0860 { 0861 return m_personPtrList; 0862 } 0863 0864 private: 0865 QList<int> m_numberList; 0866 QList<CustomGadget> m_gadgetList; 0867 QList<PersonObject *> m_personList; 0868 QList<QSharedPointer<PersonObject>> m_personPtrList; 0869 }; 0870 0871 void TestGenericTypes::propertyMacroTypes() 0872 { 0873 KTextTemplate::Engine engine; 0874 0875 qRegisterMetaType<QList<CustomGadget>>(); 0876 0877 engine.setPluginPaths({QStringLiteral(KTEXTTEMPLATE_PLUGIN_PATH)}); 0878 0879 auto objectWithProperties = new ObjectWithProperties(this); 0880 0881 KTextTemplate::Context c; 0882 c.insert(QStringLiteral("obj"), objectWithProperties); 0883 0884 { 0885 auto t1 = engine.newTemplate(QStringLiteral("{{ obj.numberList.0 }}--{{ obj.numberList.1 }}"), QStringLiteral("template1")); 0886 0887 auto result = t1->render(&c); 0888 auto expectedResult = QStringLiteral("42--7"); 0889 0890 QCOMPARE(result, expectedResult); 0891 } 0892 0893 { 0894 auto t1 = engine.newTemplate(QStringLiteral("{{ obj.gadgetList.0.fortyTwo }}--{{ obj.gadgetList.1.fortyTwo }}"), QStringLiteral("template1")); 0895 0896 auto result = t1->render(&c); 0897 auto expectedResult = QStringLiteral("42--42"); 0898 0899 QCOMPARE(result, expectedResult); 0900 } 0901 0902 { 0903 auto t1 = engine.newTemplate(QStringLiteral("{{ obj.personList.0.name }}({{ obj.personList.0.age }})" 0904 "--{{ obj.personList.1.name }}({{ obj.personList.1.age }})"), 0905 QStringLiteral("template1")); 0906 0907 auto result = t1->render(&c); 0908 auto expectedResult = QStringLiteral("Joe(20)--Mike(22)"); 0909 0910 QCOMPARE(result, expectedResult); 0911 } 0912 0913 { 0914 auto t1 = engine.newTemplate(QStringLiteral("{{ obj.personPtrList.0.name }}({{ obj.personPtrList.0.age }})" 0915 "--{{ obj.personPtrList.1.name }}({{ obj.personPtrList.1.age }})"), 0916 QStringLiteral("template1")); 0917 0918 auto result = t1->render(&c); 0919 auto expectedResult = QStringLiteral("Niall(23)--Dave(24)"); 0920 0921 QCOMPARE(result, expectedResult); 0922 } 0923 } 0924 0925 QTEST_MAIN(TestGenericTypes) 0926 #include "testgenerictypes.moc"