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 
45 Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(ThreeArray)
46 
47 Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(QtUnorderedMap)
48 
49 Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr)
50 
51 Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(std::deque)
52 
54 {
55  Q_OBJECT
56 
57 private 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 
83 class Person
84 {
85 public:
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)
104 public:
105  QString m_name;
106  int m_age = 42;
107 };
108 
109 int qHash(const Person &p) { return p.uid; }
110 
111 Q_DECLARE_METATYPE(Person)
112 Q_DECLARE_METATYPE(PersonGadget)
113 
115 if (property == QStringLiteral("name"))
116  return QString::fromStdString(object.name);
117 else if (property == QStringLiteral("age"))
118  return object.age;
120 
122 if (property == QStringLiteral("age"))
123  return object.m_age;
125 
126 class PersonObject : public QObject
127 {
128  Q_OBJECT
129  Q_PROPERTY(QString name READ name CONSTANT)
130  Q_PROPERTY(int age READ age CONSTANT)
131 public:
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 
140 private:
141  const QString m_name;
142  const int m_age; // Yeah, you wish...
143 };
144 
145 void TestGenericTypes::initTestCase()
146 {
147  // Register the handler for our custom type
148  Cutelee::registerMetaType<Person>();
149  Cutelee::registerMetaType<PersonGadget>();
150 }
151 
152 void 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 
172 static 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 
181 template <typename SequentialContainer>
182 void 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 
193 template <typename AssociativeContainer>
194 void 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 
206 template <>
207 void insertPeopleVariants<QMap<QString, QVariant>>(Cutelee::Context &c)
208 {
209  insertAssociatedPeopleVariants<QMap<QString, QVariant>>(c);
210 }
211 
212 template <>
213 void insertPeopleVariants<QHash<QString, QVariant>>(Cutelee::Context &c)
214 {
215  insertAssociatedPeopleVariants<QHash<QString, QVariant>>(c);
216 }
217 
218 template <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 
233 template <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 
248 template <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 
260 template <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 
284 template <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 
293 template <typename Container> void doTestSequentialContainer_Variant()
294 {
296 
297  insertPeopleVariants<Container>(c);
298 
301 }
302 
303 template <typename Container>
304 void 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 
328 template <typename Container>
329 void 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 
352 template <typename Container>
353 void 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 
366 void 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 
374 void TestGenericTypes::testAssociativeContainer_Variant()
375 {
376  doTestAssociativeContainer_Variant<QVariantMap>();
377  doTestAssociativeContainer_Variant<QVariantHash>(true);
378 }
379 
380 template <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 
391 template <> 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 
402 template <> 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 
414 template <typename AssociativeContainer>
415 void 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 
426 template <typename AssociativeContainer>
427 void 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 
438 template <typename Container> void doTestSequentialContainer_Type()
439 {
441 
442  insertPeople<Container>(c);
443 
446 }
447 
448 template <typename Container>
449 void 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 
462 template <typename Container>
463 void 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 
484 void 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 
497 void 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 
531 void 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 
549 void 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 
570 
571 static 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 
580 static ListVectorInt getNumberLists()
581 {
582  ListVectorInt list;
583  for (auto i = 0; i < 2; ++i) {
584  list.append(getNumbers());
585  }
586  return list;
587 }
588 
589 static 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 
598 static StackMapListVectorInt getMapStack()
599 {
600  StackMapListVectorInt stack;
601  for (auto i = 0; i < 2; ++i) {
602  stack.push(getNumberListMap());
603  }
604  return stack;
605 }
606 
607 void TestGenericTypes::testNestedContainers()
608 {
609  Cutelee::Engine engine;
610 
611  engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
612 
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 
646 class CustomObject : public QObject
647 {
648  Q_OBJECT
649 public:
650  explicit CustomObject(QObject *parent = {}) : QObject(parent) {}
651 };
652 
653 class OtherObject : public QObject
654 {
655  Q_OBJECT
656  Q_PROPERTY(CustomObject *custom READ custom CONSTANT)
657 public:
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 
666 private:
667  CustomObject *m_custom;
668 };
669 
670 void 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 
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 
711 Q_DECLARE_METATYPE(UnregisteredType)
712 
714 };
715 
716 Q_DECLARE_METATYPE(RegisteredNotListType)
717 
719 Q_UNUSED(object)
720 if (property == QStringLiteral("property"))
721  return 42;
723 
724 static QVariantList dummy(const UnregisteredType &) { return QVariantList{42}; }
725 
726 QVariant dummyLookup(const QVariant &, const QString &) { return 42; }
727 
728 void 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 
741  Cutelee::registerMetaType<RegisteredNotListType>();
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  {
752  QMetaType::registerConverter<UnregisteredType, QVariantList>(&dummy);
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 
764 Q_DECLARE_METATYPE(Person *)
765 
767 if (property == QStringLiteral("name"))
768  return QString::fromStdString(object->name);
769 else if (property == QStringLiteral("age"))
770  return object->age;
772 
773 void TestGenericTypes::testPointerNonQObject()
774 {
775  auto p = new Person("Adele", 21);
776  auto v = QVariant::fromValue(p);
777 
778  Cutelee::registerMetaType<Person *>();
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)
791 public:
792  int fortyTwo() { return 42; }
793 };
794 
795 void 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 
805 void 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");
817  c.insert(QStringLiteral("p"), QVariant::fromValue(p));
818  QCOMPARE(t1->render(&c),
819  QStringLiteral("Person: \nName: Some Name\nAge: 42"));
820 }
821 
823 {
824  Q_OBJECT
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)
828  Q_PROPERTY(
829  QVector<QSharedPointer<PersonObject>> personPtrList READ personPtrList CONSTANT)
830 
831 public:
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; }
849  QVector<QSharedPointer<PersonObject>> personPtrList()
850  {
851  return m_personPtrList;
852  }
853 
854 private:
855  QList<int> m_numberList;
856  QList<CustomGadget> m_gadgetList;
857  QVector<PersonObject *> m_personList;
858  QVector<QSharedPointer<PersonObject>> m_personPtrList;
859 };
860 
861 void 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 
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 
924 void TestGenericTypes::testJsonTypes()
925 {
926  Cutelee::Engine engine;
927  engine.setPluginPaths({QStringLiteral(CUTELEE_PLUGIN_PATH)});
928 
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 
1264 QTEST_MAIN(TestGenericTypes)
1265 #include "testgenerictypes.moc"
void setPluginPaths(const QStringList &dirs)
Definition: engine.cpp:87
The Template class is a tree of nodes which may be rendered.
Definition: template.h:94
The Context class holds the context to render a Template with.
Definition: context.h:118
void push_back(parameter_type value)
QVariant fromValue(T &&value)
void push(const T &t)
const_iterator constBegin() const const
STL namespace.
iterator insert(const T &value)
bool setProperty(const char *name, QVariant &&value)
QString number(double n, char format, int precision)
QString fromStdString(const std::string &str)
#define CUTELEE_END_LOOKUP
Definition: metatype.h:239
Q_OBJECTQ_OBJECT
#define CUTELEE_BEGIN_LOOKUP(Type)
Definition: metatype.h:213
const_iterator constEnd() const const
QString render(Context *c) const
Definition: template.cpp:74
const T & value() const const
Template newTemplate(const QString &content, const QString &name) const
Definition: engine.cpp:391
Cutelee::Engine is the main entry point for creating Cutelee Templates.
Definition: engine.h:120
Q_PROPERTY(...)
void push_back(const QJsonValue &value)
QObject(QObject *parent)
void append(QList< T > &&value)
#define CUTELEE_BEGIN_LOOKUP_PTR(Type)
Definition: metatype.h:226
iterator insert(const Key &key, const T &value)
QObject * parent() const const
void insert(const QString &name, QObject *object)
Definition: context.cpp:145