Cutelee 6.2.0
testgenerictypes.cpp
1/*
2 This file is part of the Cutelee template system.
3
4 Copyright (c) 2010 Michael Jansen <kde@michael-jansen.biz>
5 Copyright (c) 2010 Stephen Kelly <steveire@gmail.com>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either version
10 2.1 of the Licence, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library. If not, see <http://www.gnu.org/licenses/>.
19
20*/
21
22#include "engine.h"
23#include "cutelee_paths.h"
24#include "metatype.h"
25#include "template.h"
26#include "test_macros.h"
27
28#include <QtCore/QMetaType>
29#include <QtCore/QQueue>
30#include <QtCore/QStack>
31#include <QtCore/QVariant>
32#include <QtCore/QVariantHash>
33#include <QJsonValue>
34#include <QJsonArray>
35#include <QJsonObject>
36#include <QJsonDocument>
37#include <QtTest/QTest>
38
39#include "coverageobject.h"
40#include <deque>
41#include <string>
42
43#include <memory>
44
45Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(ThreeArray)
46
47Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(QtUnorderedMap)
48
49Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr)
50
51Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(std::deque)
52
53class TestGenericTypes : public CoverageObject
54{
56
57private Q_SLOTS:
58
59 void initTestCase();
60
61 void testGenericClassType();
62 void testSequentialContainer_Variant();
63 void testAssociativeContainer_Variant();
64 void testSequentialContainer_Type();
65 void testAssociativeContainer_Type();
66 void testSharedPointer();
67 void testThirdPartySharedPointer();
68 void testNestedContainers();
69
70 void testCustomQObjectDerived();
71
72 void propertyMacroTypes();
73
74 void testUnregistered();
75 void testPointerNonQObject();
76 void testQGadget();
77 void testGadgetMetaType();
78
79 void testJsonTypes();
80
81}; // class TestGenericTypes
82
83class Person
84{
85public:
86 Person() : age(0) {}
87 Person(std::string _name, int _age) : name(_name), age(_age)
88 {
89 static auto _uid = 0;
90 uid = ++_uid;
91 }
92
93 bool operator==(const Person &other) const { return uid == other.uid; }
94
95 std::string name;
96 int age;
97 int uid = 0;
98};
99
101{
102 Q_GADGET
103 Q_PROPERTY(QString name MEMBER m_name)
104public:
105 QString m_name;
106 int m_age = 42;
107};
108
109int qHash(const Person &p) { return p.uid; }
110
111Q_DECLARE_METATYPE(Person)
112Q_DECLARE_METATYPE(PersonGadget)
113
115if (property == QStringLiteral("name"))
116 return QString::fromStdString(object.name);
117else if (property == QStringLiteral("age"))
118 return object.age;
120
122if (property == QStringLiteral("age"))
123 return object.m_age;
125
126class PersonObject : public QObject
127{
129 Q_PROPERTY(QString name READ name CONSTANT)
130 Q_PROPERTY(int age READ age CONSTANT)
131public:
132 PersonObject(const QString &name, int age, QObject *parent = {})
133 : QObject(parent), m_name(name), m_age(age)
134 {
135 }
136
137 QString name() const { return m_name; }
138 int age() const { return m_age; }
139
140private:
141 const QString m_name;
142 const int m_age; // Yeah, you wish...
143};
144
145void TestGenericTypes::initTestCase()
146{
147 // Register the handler for our custom type
150}
151
152void TestGenericTypes::testGenericClassType()
153{
154 Cutelee::Engine engine;
155
156 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
157
158 auto t1 = engine.newTemplate(
159 QStringLiteral(
160 "Person: \nName: {{p.name}}\nAge: {{p.age}}\nUnknown: {{p.unknown}}"),
161 QStringLiteral("template1"));
162
163 // Check it
164 QVariantHash h;
165 Person p("Grant Lee", 2);
166 h.insert(QStringLiteral("p"), QVariant::fromValue(p));
167 Cutelee::Context c(h);
168 QCOMPARE(t1->render(&c),
169 QStringLiteral("Person: \nName: Grant Lee\nAge: 2\nUnknown: "));
170}
171
172static QMap<int, Person> getPeople()
173{
174 QMap<int, Person> people;
175 people.insert(23, Person("Claire", 23));
176 people.insert(32, Person("Grant", 32));
177 people.insert(50, Person("Alan", 50));
178 return people;
179}
180
181template <typename SequentialContainer>
182void insertPeopleVariants(Cutelee::Context &c)
183{
184 auto people = getPeople();
185 auto it = people.constBegin();
186 const auto end = people.constEnd();
187 SequentialContainer container;
188 for (; it != end; ++it)
189 container.push_back(QVariant::fromValue(it.value()));
190 c.insert(QStringLiteral("people"), QVariant::fromValue(container));
191}
192
193template <typename AssociativeContainer>
194void insertAssociatedPeopleVariants(Cutelee::Context &c)
195{
196 auto people = getPeople();
197 auto it = people.constBegin();
198 const auto end = people.constEnd();
199 AssociativeContainer container;
200 for (; it != end; ++it)
201 container.insert(QString::number(it.key()),
202 QVariant::fromValue(it.value()));
203 c.insert(QStringLiteral("people"), QVariant::fromValue(container));
204}
205
206template <>
207void insertPeopleVariants<QMap<QString, QVariant>>(Cutelee::Context &c)
208{
209 insertAssociatedPeopleVariants<QMap<QString, QVariant>>(c);
210}
211
212template <>
213void insertPeopleVariants<QHash<QString, QVariant>>(Cutelee::Context &c)
214{
215 insertAssociatedPeopleVariants<QHash<QString, QVariant>>(c);
216}
217
218template <typename Container> void testSequentialIteration(Cutelee::Context &c)
219{
220 Cutelee::Engine engine;
221
222 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
223
224 {
225 Cutelee::Template t1 = engine.newTemplate(
226 QStringLiteral(
227 "{% for person in people %}{{ person.name }},{% endfor %}"),
228 QStringLiteral("people_template"));
229 QCOMPARE(t1->render(&c), QStringLiteral("Claire,Grant,Alan,"));
230 }
231}
232
233template <typename Container> void testSequentialIndexing(Cutelee::Context &c)
234{
235 Cutelee::Engine engine;
236
237 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
238
239 {
240 Cutelee::Template t1 = engine.newTemplate(
241 QStringLiteral(
242 "{{ people.0.name }},{{ people.1.name }},{{ people.2.name }},"),
243 QStringLiteral("people_template"));
244 QCOMPARE(t1->render(&c), QStringLiteral("Claire,Grant,Alan,"));
245 }
246}
247
248template <typename Container> struct SequentialContainerTester {
249 static void iteration(Cutelee::Context &c)
250 {
251 testSequentialIteration<Container>(c);
252 }
253
254 static void indexing(Cutelee::Context &c)
255 {
256 testSequentialIndexing<Container>(c);
257 }
258};
259
260template <typename T> struct SequentialContainerTester<QSet<T>> {
261 static void iteration(Cutelee::Context &c)
262 {
263 Cutelee::Engine engine;
264
265 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
266
267 Cutelee::Template t1 = engine.newTemplate(
268 QStringLiteral(
269 "{% for person in people %}{{ person.name }},{% endfor %}"),
270 QStringLiteral("people_template"));
271 auto result = t1->render(&c);
272 QStringList output{QStringLiteral("Claire,"), QStringLiteral("Grant,"),
273 QStringLiteral("Alan,")};
274 Q_FOREACH (const QString &s, output) {
275 QVERIFY(result.contains(s));
276 }
277
278 QCOMPARE(result.length(), output.join(QString()).length());
279 }
280
281 static void indexing(Cutelee::Context) {}
282};
283
284template <typename T> struct SequentialContainerTester<std::list<T>> {
285 static void iteration(Cutelee::Context &c)
286 {
287 testSequentialIteration<std::list<T>>(c);
288 }
289
290 static void indexing(Cutelee::Context) {}
291};
292
293template <typename Container> void doTestSequentialContainer_Variant()
294{
296
297 insertPeopleVariants<Container>(c);
298
299 SequentialContainerTester<Container>::iteration(c);
300 SequentialContainerTester<Container>::indexing(c);
301}
302
303template <typename Container>
304void testAssociativeValues(Cutelee::Context &c, bool unordered = {})
305{
306 Cutelee::Engine engine;
307
308 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
309
310 {
311 Cutelee::Template t1 = engine.newTemplate(
312 QStringLiteral("{% for person in people.values %}({{ person.name }}:{{ "
313 "person.age }}),{% endfor %}"),
314 QStringLiteral("people_template"));
315
316 auto result = t1->render(&c);
317 if (!unordered)
318 QCOMPARE(result, QStringLiteral("(Claire:23),(Grant:32),(Alan:50),"));
319 else {
320 QVERIFY(result.size() == 33);
321 QVERIFY(result.contains(QStringLiteral("(Claire:23),")));
322 QVERIFY(result.contains(QStringLiteral("(Grant:32),")));
323 QVERIFY(result.contains(QStringLiteral("(Alan:50),")));
324 }
325 }
326}
327
328template <typename Container>
329void testAssociativeItems(Cutelee::Context &c, bool unordered)
330{
331 Cutelee::Engine engine;
332
333 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
334
335 {
336 Cutelee::Template t1 = engine.newTemplate(
337 QStringLiteral("{% for item in people.items %}({{ item.1.name }}:{{ "
338 "item.1.age }}),{% endfor %}"),
339 QStringLiteral("people_template"));
340 auto result = t1->render(&c);
341 if (!unordered)
342 QCOMPARE(result, QStringLiteral("(Claire:23),(Grant:32),(Alan:50),"));
343 else {
344 QVERIFY(result.size() == 33);
345 QVERIFY(result.contains(QStringLiteral("(Claire:23),")));
346 QVERIFY(result.contains(QStringLiteral("(Grant:32),")));
347 QVERIFY(result.contains(QStringLiteral("(Alan:50),")));
348 }
349 }
350}
351
352template <typename Container>
353void doTestAssociativeContainer_Variant(bool unordered = {})
354{
355 Cutelee::Engine engine;
356
357 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
358
360
361 insertPeopleVariants<Container>(c);
362 testAssociativeValues<Container>(c, unordered);
363 testAssociativeItems<Container>(c, unordered);
364}
365
366void TestGenericTypes::testSequentialContainer_Variant()
367{
368 doTestSequentialContainer_Variant<QVariantList>();
369 doTestSequentialContainer_Variant<QVector<QVariant>>();
370 doTestSequentialContainer_Variant<QStack<QVariant>>();
371 doTestSequentialContainer_Variant<QQueue<QVariant>>();
372}
373
374void TestGenericTypes::testAssociativeContainer_Variant()
375{
376 doTestAssociativeContainer_Variant<QVariantMap>();
377 doTestAssociativeContainer_Variant<QVariantHash>(true);
378}
379
380template <typename SequentialContainer> void insertPeople(Cutelee::Context &c)
381{
382 auto people = getPeople();
383 auto it = people.constBegin();
384 const auto end = people.constEnd();
385 SequentialContainer container;
386 for (; it != end; ++it)
387 container.insert(container.end(), it.value());
388 c.insert(QStringLiteral("people"), QVariant::fromValue(container));
389}
390
391template <> void insertPeople<QSet<Person>>(Cutelee::Context &c)
392{
393 auto people = getPeople();
394 auto it = people.constBegin();
395 const auto end = people.constEnd();
396 QSet<Person> container;
397 for (; it != end; ++it)
398 container.insert(it.value());
399 c.insert(QStringLiteral("people"), QVariant::fromValue(container));
400}
401
402template <> void insertPeople<ThreeArray<Person>>(Cutelee::Context &c)
403{
404 auto people = getPeople();
405 auto it = people.constBegin();
406 ThreeArray<Person> container;
407 for (auto i = 0; i < 3; ++i, ++it) {
408 Q_ASSERT(it != people.constEnd());
409 container[i] = it.value();
410 }
411 c.insert(QStringLiteral("people"), QVariant::fromValue(container));
412}
413
414template <typename AssociativeContainer>
415void insertAssociatedPeople(Cutelee::Context &c)
416{
417 auto people = getPeople();
418 auto it = people.constBegin();
419 const auto end = people.constEnd();
420 AssociativeContainer container;
421 for (; it != end; ++it)
422 container[QString::number(it.key())] = it.value();
423 c.insert(QStringLiteral("people"), QVariant::fromValue(container));
424}
425
426template <typename AssociativeContainer>
427void insertAssociatedPeople_Number(Cutelee::Context &c)
428{
429 auto people = getPeople();
430 auto it = people.constBegin();
431 const auto end = people.constEnd();
432 AssociativeContainer container;
433 for (; it != end; ++it)
434 container[it.key()] = it.value();
435 c.insert(QStringLiteral("people"), QVariant::fromValue(container));
436}
437
438template <typename Container> void doTestSequentialContainer_Type()
439{
441
442 insertPeople<Container>(c);
443
444 SequentialContainerTester<Container>::iteration(c);
445 SequentialContainerTester<Container>::indexing(c);
446}
447
448template <typename Container>
449void doTestAssociativeContainer_Type(bool unordered = {})
450{
451 Cutelee::Engine engine;
452
453 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
454
456
457 insertAssociatedPeople<Container>(c);
458 testAssociativeValues<Container>(c, unordered);
459 testAssociativeItems<Container>(c, unordered);
460}
461
462template <typename Container>
463void doTestAssociativeContainer_Type_Number(bool unordered = {})
464{
465 Cutelee::Engine engine;
466
467 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
468
470
471 insertAssociatedPeople_Number<Container>(c);
472 testAssociativeValues<Container>(c, unordered);
473 testAssociativeItems<Container>(c, unordered);
474
475 {
477 = engine.newTemplate(QStringLiteral("{{ people.23.name }}"),
478 QStringLiteral("claire_template"));
479 auto result = t1->render(&c);
480 QCOMPARE(result, QStringLiteral("Claire"));
481 }
482}
483
484void TestGenericTypes::testSequentialContainer_Type()
485{
486 doTestSequentialContainer_Type<QList<Person>>();
487 doTestSequentialContainer_Type<QVector<Person>>();
488 doTestSequentialContainer_Type<QStack<Person>>();
489 doTestSequentialContainer_Type<QQueue<Person>>();
490 doTestSequentialContainer_Type<QSet<Person>>();
491 doTestSequentialContainer_Type<std::deque<Person>>();
492 doTestSequentialContainer_Type<std::vector<Person>>();
493 doTestSequentialContainer_Type<std::list<Person>>();
494 doTestSequentialContainer_Type<ThreeArray<Person>>();
495}
496
497void TestGenericTypes::testAssociativeContainer_Type()
498{
499 doTestAssociativeContainer_Type<QMap<QString, Person>>();
500 doTestAssociativeContainer_Type_Number<QMap<qint16, Person>>();
501 doTestAssociativeContainer_Type_Number<QMap<qint32, Person>>();
502 doTestAssociativeContainer_Type_Number<QMap<qint64, Person>>();
503 doTestAssociativeContainer_Type_Number<QMap<quint16, Person>>();
504 doTestAssociativeContainer_Type_Number<QMap<quint32, Person>>();
505 doTestAssociativeContainer_Type_Number<QMap<quint64, Person>>();
506 doTestAssociativeContainer_Type<QHash<QString, Person>>(true);
507 doTestAssociativeContainer_Type_Number<QHash<qint16, Person>>(true);
508 doTestAssociativeContainer_Type_Number<QHash<qint32, Person>>(true);
509 doTestAssociativeContainer_Type_Number<QHash<qint64, Person>>(true);
510 doTestAssociativeContainer_Type_Number<QHash<quint16, Person>>(true);
511 doTestAssociativeContainer_Type_Number<QHash<quint32, Person>>(true);
512 doTestAssociativeContainer_Type_Number<QHash<quint64, Person>>(true);
513
514 doTestAssociativeContainer_Type<std::map<QString, Person>>();
515 doTestAssociativeContainer_Type_Number<std::map<qint16, Person>>();
516 doTestAssociativeContainer_Type_Number<std::map<qint32, Person>>();
517 doTestAssociativeContainer_Type_Number<std::map<qint64, Person>>();
518 doTestAssociativeContainer_Type_Number<std::map<quint16, Person>>();
519 doTestAssociativeContainer_Type_Number<std::map<quint32, Person>>();
520 doTestAssociativeContainer_Type_Number<std::map<quint64, Person>>();
521
522 doTestAssociativeContainer_Type<QtUnorderedMap<QString, Person>>(true);
523 doTestAssociativeContainer_Type_Number<QtUnorderedMap<qint16, Person>>(true);
524 doTestAssociativeContainer_Type_Number<QtUnorderedMap<qint32, Person>>(true);
525 doTestAssociativeContainer_Type_Number<QtUnorderedMap<qint64, Person>>(true);
526 doTestAssociativeContainer_Type_Number<QtUnorderedMap<quint16, Person>>(true);
527 doTestAssociativeContainer_Type_Number<QtUnorderedMap<quint32, Person>>(true);
528 doTestAssociativeContainer_Type_Number<QtUnorderedMap<quint64, Person>>(true);
529}
530
531void TestGenericTypes::testSharedPointer()
532{
533 Cutelee::Engine engine;
534
535 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
536
537 auto t1 = engine.newTemplate(QStringLiteral("{{ p.name }} {{ p.age }}"),
538 QStringLiteral("template1"));
539
540 // Check it
541 QVariantHash h;
542 std::shared_ptr<PersonObject> p(
543 new PersonObject(QStringLiteral("Grant Lee"), 2));
544 h.insert(QStringLiteral("p"), QVariant::fromValue(p));
545 Cutelee::Context c(h);
546 QCOMPARE(t1->render(&c), QStringLiteral("Grant Lee 2"));
547}
548
549void TestGenericTypes::testThirdPartySharedPointer()
550{
551 Cutelee::Engine engine;
552
553 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
554
555 auto t1 = engine.newTemplate(QStringLiteral("{{ p.name }} {{ p.age }}"),
556 QStringLiteral("template1"));
557
558 // Check it
559 QVariantHash h;
560 std::shared_ptr<PersonObject> p(
561 new PersonObject(QStringLiteral("Grant Lee"), 2));
562 h.insert(QStringLiteral("p"), QVariant::fromValue(p));
563 Cutelee::Context c(h);
564 QCOMPARE(t1->render(&c), QStringLiteral("Grant Lee 2"));
565}
566
567typedef QList<QVector<qint16>> ListVectorInt;
568typedef QMap<int, QList<QVector<qint16>>> MapListVectorInt;
569typedef QStack<QMap<int, QList<QVector<qint16>>>> StackMapListVectorInt;
570
571static QVector<qint16> getNumbers()
572{
573 static auto n = 0;
574 QVector<qint16> nums;
575 nums.push_back(++n);
576 nums.push_back(++n);
577 return nums;
578}
579
580static ListVectorInt getNumberLists()
581{
582 ListVectorInt list;
583 for (auto i = 0; i < 2; ++i) {
584 list.append(getNumbers());
585 }
586 return list;
587}
588
589static MapListVectorInt getNumberListMap()
590{
591 MapListVectorInt map;
592 for (auto i = 0; i < 2; ++i) {
593 map.insert(i, getNumberLists());
594 }
595 return map;
596}
597
598static StackMapListVectorInt getMapStack()
599{
600 StackMapListVectorInt stack;
601 for (auto i = 0; i < 2; ++i) {
602 stack.push(getNumberListMap());
603 }
604 return stack;
605}
606
607void TestGenericTypes::testNestedContainers()
608{
609 Cutelee::Engine engine;
610
611 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
612
613 Cutelee::Context c;
614 c.insert(QStringLiteral("stack"), QVariant::fromValue(getMapStack()));
615
616#if defined(Q_CC_MSVC)
617// MSVC doesn't like static string concatenations like L"foo" "bar", as
618// results from QStringLiteral, so use QLatin1String here instead.
619#define STRING_LITERAL QLatin1String
620#else
621#define STRING_LITERAL QStringLiteral
622#endif
623 auto t1 = engine.newTemplate(
624 STRING_LITERAL("{% for map in stack %}"
625 "(M {% for key, list in map.items %}"
626 "({{ key }} : (L {% for vector in list %}"
627 "(V {% for number in vector %}"
628 "{{ number }},"
629 "{% endfor %}),"
630 "{% endfor %}),"
631 "{% endfor %}),"
632 "{% endfor %}"),
633 QStringLiteral("template1"));
634
635#undef STRING_LITERAL
636
637 auto result = t1->render(&c);
638
639 auto expectedResult = QStringLiteral(
640 "(M (0 : (L (V 1,2,),(V 3,4,),),(1 : (L (V 5,6,),(V 7,8,),),),(M (0 : (L "
641 "(V 9,10,),(V 11,12,),),(1 : (L (V 13,14,),(V 15,16,),),),");
642
643 QCOMPARE(result, expectedResult);
644}
645
646class CustomObject : public QObject
647{
649public:
650 explicit CustomObject(QObject *parent = {}) : QObject(parent) {}
651};
652
653class OtherObject : public QObject
654{
656 Q_PROPERTY(CustomObject *custom READ custom CONSTANT)
657public:
658 explicit OtherObject(QObject *parent = {})
659 : QObject(parent), m_custom(new CustomObject(this))
660 {
661 m_custom->setProperty("nestedProp", QStringLiteral("nestedValue"));
662 }
663
664 CustomObject *custom() { return m_custom; }
665
666private:
667 CustomObject *m_custom;
668};
669
670void TestGenericTypes::testCustomQObjectDerived()
671{
672 Cutelee::Engine engine;
673
674 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
675
676 auto customObject = new CustomObject(this);
677 customObject->setProperty("someProp", QStringLiteral("propValue"));
678
679 Cutelee::Context c;
680 c.insert(QStringLiteral("custom"), QVariant::fromValue(customObject));
681
682 {
683 auto t1 = engine.newTemplate(QStringLiteral("{{ custom.someProp }}"),
684 QStringLiteral("template1"));
685
686 auto result = t1->render(&c);
687 auto expectedResult = QStringLiteral("propValue");
688
689 QCOMPARE(result, expectedResult);
690 }
691
692 auto other = new OtherObject(this);
693
694 c.insert(QStringLiteral("other"), other);
695
696 {
697 auto t1
698 = engine.newTemplate(QStringLiteral("{{ other.custom.nestedProp }}"),
699 QStringLiteral("template1"));
700
701 auto result = t1->render(&c);
702 auto expectedResult = QStringLiteral("nestedValue");
703
704 QCOMPARE(result, expectedResult);
705 }
706}
707
709};
710
711Q_DECLARE_METATYPE(UnregisteredType)
712
715
716Q_DECLARE_METATYPE(RegisteredNotListType)
717
719Q_UNUSED(object)
720if (property == QStringLiteral("property"))
721 return 42;
723
724static QVariantList dummy(const UnregisteredType &) { return QVariantList{42}; }
725
726QVariant dummyLookup(const QVariant &, const QString &) { return 42; }
727
728void TestGenericTypes::testUnregistered()
729{
730
731 {
732 UnregisteredType unregType;
733 auto v = QVariant::fromValue(unregType);
734
735 auto result = Cutelee::MetaType::lookup(v, QStringLiteral("property"));
736 QVERIFY(!result.isValid());
737
738 QVERIFY(!v.canConvert<QVariantList>());
739 }
740
742
743 {
744 RegisteredNotListType nonListType;
745 auto v = QVariant::fromValue(nonListType);
746 auto result = Cutelee::MetaType::lookup(v, QStringLiteral("property"));
747 QVERIFY(result.isValid());
748 QVERIFY(!v.canConvert<QVariantList>());
749 }
750
751 {
753 UnregisteredType unregType;
754 auto v = QVariant::fromValue(unregType);
755 auto result = Cutelee::MetaType::lookup(v, QStringLiteral("property"));
756 QVERIFY(!result.isValid());
757 }
758
759 // Only do this in release mode?
760 // Cutelee::MetaType::registerLookUpOperator(0, dummyLookup);
761 // Cutelee::MetaType::registerToVariantListOperator(0, dummy);
762}
763
764Q_DECLARE_METATYPE(Person *)
765
767if (property == QStringLiteral("name"))
768 return QString::fromStdString(object->name);
769else if (property == QStringLiteral("age"))
770 return object->age;
772
773void TestGenericTypes::testPointerNonQObject()
774{
775 auto p = new Person("Adele", 21);
776 auto v = QVariant::fromValue(p);
777
779
780 auto result = Cutelee::MetaType::lookup(v, QStringLiteral("name"));
781
782 QCOMPARE(result.toString(), QStringLiteral("Adele"));
783
784 delete p;
785}
786
788{
789 Q_GADGET
790 Q_PROPERTY(int fortyTwo READ fortyTwo)
791public:
792 int fortyTwo() { return 42; }
793};
794
795void TestGenericTypes::testQGadget()
796{
797 CustomGadget g;
798 auto v = QVariant::fromValue(g);
799
800 auto result = Cutelee::MetaType::lookup(v, QStringLiteral("fortyTwo"));
801
802 QCOMPARE(result.value<int>(), 42);
803}
804
805void TestGenericTypes::testGadgetMetaType()
806{
807 Cutelee::Engine engine;
808 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
809
810 auto t1 = engine.newTemplate(
811 QStringLiteral("Person: \nName: {{p.name}}\nAge: {{p.age}}"),
812 QStringLiteral("template1"));
813
814 PersonGadget p;
815 p.m_name = QStringLiteral("Some Name");
816 Cutelee::Context c;
817 c.insert(QStringLiteral("p"), QVariant::fromValue(p));
818 QCOMPARE(t1->render(&c),
819 QStringLiteral("Person: \nName: Some Name\nAge: 42"));
820}
821
822class ObjectWithProperties : public QObject
823{
825 Q_PROPERTY(QList<int> numberList READ numberList CONSTANT)
826 Q_PROPERTY(QList<CustomGadget> gadgetList READ gadgetList CONSTANT)
827 Q_PROPERTY(QVector<PersonObject *> personList READ personList CONSTANT)
829 QVector<QSharedPointer<PersonObject>> personPtrList READ personPtrList CONSTANT)
830
831public:
832 ObjectWithProperties(QObject *parent = {}) : QObject(parent)
833 {
834 m_numberList.push_back(42);
835 m_numberList.push_back(7);
836 m_gadgetList.push_back(CustomGadget{});
837 m_gadgetList.push_back(CustomGadget{});
838 m_personList.push_back(new PersonObject{QStringLiteral("Joe"), 20});
839 m_personList.push_back(new PersonObject{QStringLiteral("Mike"), 22});
840 m_personPtrList.push_back(
841 QSharedPointer<PersonObject>(new PersonObject{QStringLiteral("Niall"), 23}));
842 m_personPtrList.push_back(
843 QSharedPointer<PersonObject>(new PersonObject{QStringLiteral("Dave"), 24}));
844 }
845
846 QList<int> numberList() { return m_numberList; }
847 QList<CustomGadget> gadgetList() { return m_gadgetList; }
848 QVector<PersonObject *> personList() { return m_personList; }
850 {
851 return m_personPtrList;
852 }
853
854private:
855 QList<int> m_numberList;
856 QList<CustomGadget> m_gadgetList;
857 QVector<PersonObject *> m_personList;
859};
860
861void TestGenericTypes::propertyMacroTypes()
862{
863 Cutelee::Engine engine;
864
865 qRegisterMetaType<QList<CustomGadget>>();
866
867 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
868
869 auto objectWithProperties = new ObjectWithProperties(this);
870
871 Cutelee::Context c;
872 c.insert(QStringLiteral("obj"), objectWithProperties);
873
874 {
875 auto t1 = engine.newTemplate(
876 QStringLiteral("{{ obj.numberList.0 }}--{{ obj.numberList.1 }}"),
877 QStringLiteral("template1"));
878
879 auto result = t1->render(&c);
880 auto expectedResult = QStringLiteral("42--7");
881
882 QCOMPARE(result, expectedResult);
883 }
884
885 {
886 auto t1 = engine.newTemplate(
887 QStringLiteral(
888 "{{ obj.gadgetList.0.fortyTwo }}--{{ obj.gadgetList.1.fortyTwo }}"),
889 QStringLiteral("template1"));
890
891 auto result = t1->render(&c);
892 auto expectedResult = QStringLiteral("42--42");
893
894 QCOMPARE(result, expectedResult);
895 }
896
897 {
898 auto t1 = engine.newTemplate(
899 QStringLiteral(
900 "{{ obj.personList.0.name }}({{ obj.personList.0.age }})"
901 "--{{ obj.personList.1.name }}({{ obj.personList.1.age }})"),
902 QStringLiteral("template1"));
903
904 auto result = t1->render(&c);
905 auto expectedResult = QStringLiteral("Joe(20)--Mike(22)");
906
907 QCOMPARE(result, expectedResult);
908 }
909
910 {
911 auto t1 = engine.newTemplate(
912 QStringLiteral(
913 "{{ obj.personPtrList.0.name }}({{ obj.personPtrList.0.age }})"
914 "--{{ obj.personPtrList.1.name }}({{ obj.personPtrList.1.age }})"),
915 QStringLiteral("template1"));
916
917 auto result = t1->render(&c);
918 auto expectedResult = QStringLiteral("Niall(23)--Dave(24)");
919
920 QCOMPARE(result, expectedResult);
921 }
922}
923
924void TestGenericTypes::testJsonTypes()
925{
926 Cutelee::Engine engine;
927 engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
928
929 Cutelee::Context c;
930
931 QJsonArray arr;
932 arr.push_back(QJsonObject({
933 {QStringLiteral("name"), QStringLiteral("Joe")},
934 {QStringLiteral("age"), 20}
935 }));
936 QJsonObject obj({
937 {QStringLiteral("name"), QStringLiteral("Mike")},
938 {QStringLiteral("age"), 22}
939 });
940 arr.push_back(obj);
941
942 c.insert(QStringLiteral("arr"), arr);
943 c.insert(QStringLiteral("obj"), obj);
944
945 {
946 auto t = engine.newTemplate(
947 QStringLiteral("{{ arr.count }}"),
948 QStringLiteral("template"));
949
950 auto result = t->render(&c);
951 auto expectedResult = QStringLiteral("2");
952
953 QCOMPARE(result, expectedResult);
954 }
955
956 {
957 auto t = engine.newTemplate(
958 QStringLiteral("{{ arr.1.name }}({{ arr.1.age }})"),
959 QStringLiteral("template"));
960
961 auto result = t->render(&c);
962 auto expectedResult = QStringLiteral("Mike(22)");
963
964 QCOMPARE(result, expectedResult);
965 }
966
967 {
968 auto t = engine.newTemplate(
969 QStringLiteral("{% for person in arr %}{{ person.name }}({{ person.age }})\n{% endfor %}"),
970 QStringLiteral("template"));
971
972 auto result = t->render(&c);
973 auto expectedResult = QStringLiteral("Joe(20)\nMike(22)\n");
974
975 QCOMPARE(result, expectedResult);
976 }
977
978 {
979 auto t = engine.newTemplate(
980 QStringLiteral("{% for name,age in arr %}{{ name }}({{ age }})\n{% endfor %}"),
981 QStringLiteral("template"));
982
983 auto result = t->render(&c);
984 auto expectedResult = QStringLiteral("Joe(20)\nMike(22)\n");
985
986 QCOMPARE(result, expectedResult);
987 }
988
989 {
990 auto t = engine.newTemplate(
991 QStringLiteral("{{ obj.count }}"),
992 QStringLiteral("template"));
993
994 auto result = t->render(&c);
995 auto expectedResult = QStringLiteral("2");
996
997 QCOMPARE(result, expectedResult);
998 }
999
1000 {
1001 auto t = engine.newTemplate(
1002 QStringLiteral("{{ obj.name }}({{ obj.age }})"),
1003 QStringLiteral("template"));
1004
1005 auto result = t->render(&c);
1006 auto expectedResult = QStringLiteral("Mike(22)");
1007
1008 QCOMPARE(result, expectedResult);
1009 }
1010
1011 {
1012 auto t = engine.newTemplate(
1013 QStringLiteral("{% for key in obj.keys %}{{ key }}\n{% endfor %}"),
1014 QStringLiteral("template"));
1015
1016 auto result = t->render(&c);
1017
1018 QVERIFY(result == QStringLiteral("name\nage\n") || result == QStringLiteral("age\nname\n"));
1019 }
1020
1021 {
1022 auto t = engine.newTemplate(
1023 QStringLiteral("{% for val in obj.values %}{{ val }}\n{% endfor %}"),
1024 QStringLiteral("template"));
1025
1026 auto result = t->render(&c);
1027
1028 QVERIFY(result == QStringLiteral("Mike\n22\n") || result == QStringLiteral("22\nMike\n"));
1029 }
1030
1031 {
1032 auto t = engine.newTemplate(
1033 QStringLiteral("{% for item in obj.items %}{{ item.0 }}:{{ item.1 }}\n{% endfor %}"),
1034 QStringLiteral("template"));
1035
1036 auto result = t->render(&c);
1037
1038 QVERIFY(result == QStringLiteral("age:22\nname:Mike\n") || result == QStringLiteral("name:Mike\nage:22\n"));
1039 }
1040
1041 QJsonDocument arrDoc(arr);
1042 c.insert(QStringLiteral("arrDoc"), arrDoc);
1043
1044 {
1045 auto t = engine.newTemplate(
1046 QStringLiteral("{{ arrDoc.count }}"),
1047 QStringLiteral("template"));
1048
1049 auto result = t->render(&c);
1050 auto expectedResult = QStringLiteral("2");
1051
1052 QCOMPARE(result, expectedResult);
1053 }
1054
1055 {
1056 auto t = engine.newTemplate(
1057 QStringLiteral("{{ arrDoc.1.name }}({{ arrDoc.1.age }})"),
1058 QStringLiteral("template"));
1059
1060 auto result = t->render(&c);
1061 auto expectedResult = QStringLiteral("Mike(22)");
1062
1063 QCOMPARE(result, expectedResult);
1064 }
1065
1066 {
1067 auto t = engine.newTemplate(
1068 QStringLiteral("{% for person in arrDoc %}{{ person.name }}({{ person.age }})\n{% endfor %}"),
1069 QStringLiteral("template"));
1070
1071 auto result = t->render(&c);
1072 auto expectedResult = QStringLiteral("Joe(20)\nMike(22)\n");
1073
1074 QCOMPARE(result, expectedResult);
1075 }
1076
1077 {
1078 auto t = engine.newTemplate(
1079 QStringLiteral("{% for name,age in arrDoc %}{{ name }}({{ age }})\n{% endfor %}"),
1080 QStringLiteral("template"));
1081
1082 auto result = t->render(&c);
1083 auto expectedResult = QStringLiteral("Joe(20)\nMike(22)\n");
1084
1085 QCOMPARE(result, expectedResult);
1086 }
1087
1088 QJsonDocument objDoc(obj);
1089 c.insert(QStringLiteral("objDoc"), objDoc);
1090
1091 {
1092 auto t = engine.newTemplate(
1093 QStringLiteral("{{ objDoc.count }}"),
1094 QStringLiteral("template"));
1095
1096 auto result = t->render(&c);
1097 auto expectedResult = QStringLiteral("2");
1098
1099 QCOMPARE(result, expectedResult);
1100 }
1101
1102 {
1103 auto t = engine.newTemplate(
1104 QStringLiteral("{{ objDoc.name }}({{ objDoc.age }})"),
1105 QStringLiteral("template"));
1106
1107 auto result = t->render(&c);
1108 auto expectedResult = QStringLiteral("Mike(22)");
1109
1110 QCOMPARE(result, expectedResult);
1111 }
1112
1113 {
1114 auto t = engine.newTemplate(
1115 QStringLiteral("{% for key in objDoc.keys %}{{ key }}\n{% endfor %}"),
1116 QStringLiteral("template"));
1117
1118 auto result = t->render(&c);
1119
1120 QVERIFY(result == QStringLiteral("name\nage\n") || result == QStringLiteral("age\nname\n"));
1121 }
1122
1123 {
1124 auto t = engine.newTemplate(
1125 QStringLiteral("{% for val in objDoc.values %}{{ val }}\n{% endfor %}"),
1126 QStringLiteral("template"));
1127
1128 auto result = t->render(&c);
1129
1130 QVERIFY(result == QStringLiteral("Mike\n22\n") || result == QStringLiteral("22\nMike\n"));
1131 }
1132
1133 {
1134 auto t = engine.newTemplate(
1135 QStringLiteral("{% for item in objDoc.items %}{{ item.0 }}:{{ item.1 }}\n{% endfor %}"),
1136 QStringLiteral("template"));
1137
1138 auto result = t->render(&c);
1139
1140 QVERIFY(result == QStringLiteral("age:22\nname:Mike\n") || result == QStringLiteral("name:Mike\nage:22\n"));
1141 }
1142
1143 QJsonObject emptyObj;
1144 c.insert(QStringLiteral("emptyObj"), emptyObj);
1145
1146 {
1147 auto t = engine.newTemplate(
1148 QStringLiteral("{{ emptyObj.name }}"),
1149 QStringLiteral("template"));
1150
1151 auto result = t->render(&c);
1152 auto expectedResult = QStringLiteral("");
1153
1154 QCOMPARE(result, expectedResult);
1155 }
1156
1157 QJsonArray emptyArr;
1158 c.insert(QStringLiteral("emptyArr"), emptyArr);
1159
1160 {
1161 auto t = engine.newTemplate(
1162 QStringLiteral("{{ emptyArr.1 }}"),
1163 QStringLiteral("template"));
1164
1165 auto result = t->render(&c);
1166 auto expectedResult = QStringLiteral("");
1167
1168 QCOMPARE(result, expectedResult);
1169 }
1170
1171 QJsonDocument emptyDoc;
1172 c.insert(QStringLiteral("emptyDoc"), emptyDoc);
1173
1174 {
1175 auto t = engine.newTemplate(
1176 QStringLiteral("{{ emptyDoc }}"),
1177 QStringLiteral("template"));
1178
1179 auto result = t->render(&c);
1180 auto expectedResult = QStringLiteral("");
1181
1182 QCOMPARE(result, expectedResult);
1183 }
1184
1185 c.insert(QStringLiteral("valBool"), QJsonValue(true));
1186
1187 {
1188 auto t = engine.newTemplate(
1189 QStringLiteral("{{ valBool }}"),
1190 QStringLiteral("template"));
1191
1192 auto result = t->render(&c);
1193 auto expectedResult = QStringLiteral("true");
1194
1195 QCOMPARE(result, expectedResult);
1196 }
1197
1198 c.insert(QStringLiteral("valDouble"), QJsonValue(15));
1199
1200 {
1201 auto t = engine.newTemplate(
1202 QStringLiteral("{{ valDouble }}"),
1203 QStringLiteral("template"));
1204
1205 auto result = t->render(&c);
1206 auto expectedResult = QStringLiteral("15");
1207
1208 QCOMPARE(result, expectedResult);
1209 }
1210
1211 c.insert(QStringLiteral("valString"), QJsonValue(QStringLiteral("Sapere aude")));
1212
1213 {
1214 auto t = engine.newTemplate(
1215 QStringLiteral("{{ valString }}"),
1216 QStringLiteral("template"));
1217
1218 auto result = t->render(&c);
1219 auto expectedResult = QStringLiteral("Sapere aude");
1220
1221 QCOMPARE(result, expectedResult);
1222 }
1223
1224 c.insert(QStringLiteral("valArray"), QJsonValue(arr));
1225
1226 {
1227 auto t = engine.newTemplate(
1228 QStringLiteral("{% for person in valArray %}{{ person.name }}({{ person.age }})\n{% endfor %}"),
1229 QStringLiteral("template"));
1230
1231 auto result = t->render(&c);
1232 auto expectedResult = QStringLiteral("Joe(20)\nMike(22)\n");
1233
1234 QCOMPARE(result, expectedResult);
1235 }
1236
1237 c.insert(QStringLiteral("valObj"), QJsonValue(obj));
1238
1239 {
1240 auto t = engine.newTemplate(
1241 QStringLiteral("{{ valObj.name }}({{ valObj.age }})"),
1242 QStringLiteral("template"));
1243
1244 auto result = t->render(&c);
1245 auto expectedResult = QStringLiteral("Mike(22)");
1246
1247 QCOMPARE(result, expectedResult);
1248 }
1249
1250 c.insert(QStringLiteral("valNull"), QJsonValue());
1251
1252 {
1253 auto t = engine.newTemplate(
1254 QStringLiteral("{{ valNull }}"),
1255 QStringLiteral("template"));
1256
1257 auto result = t->render(&c);
1258 auto expectedResult = QStringLiteral("");
1259
1260 QCOMPARE(result, expectedResult);
1261 }
1262}
1263
1264QTEST_MAIN(TestGenericTypes)
1265#include "testgenerictypes.moc"
The Context class holds the context to render a Template with.
Definition context.h:119
void insert(const QString &name, QObject *object)
Definition context.cpp:145
Cutelee::Engine is the main entry point for creating Cutelee Templates.
Definition engine.h:121
void setPluginPaths(const QStringList &dirs)
Definition engine.cpp:87
Template newTemplate(const QString &content, const QString &name) const
Definition engine.cpp:391
The Template class is a tree of nodes which may be rendered.
Definition template.h:95
QString render(Context *c) const
Definition template.cpp:74
#define CUTELEE_BEGIN_LOOKUP(Type)
Definition metatype.h:213
#define CUTELEE_END_LOOKUP
Definition metatype.h:239
#define CUTELEE_BEGIN_LOOKUP_PTR(Type)
Definition metatype.h:226
int registerMetaType()
Registers the type RealType with the metatype system.
Definition metatype.h:182
void push_back(const QJsonValue &value)
void append(QList< T > &&value)
void push_back(QList< T >::parameter_type value)
const T & value() const const
QMap< Key, T >::const_iterator constBegin() const const
QMap< Key, T >::const_iterator constEnd() const const
QMap< Key, T >::iterator insert(QMap< Key, T >::const_iterator pos, const Key &key, const T &value)
bool registerConverter()
QObject(QObject *parent)
Q_OBJECTQ_OBJECT
Q_PROPERTY(...)
Q_SLOTSQ_SLOTS
QObject * parent() const const
QSet< T >::iterator insert(QSet< T >::const_iterator it, const T &value)
void push(T &&t)
QString fromStdString(const std::string &str)
QString number(double n, char format, int precision)
QString join(QChar separator) const const
QVariant fromValue(T &&value)