File indexing completed on 2024-11-10 04:40:12

0001 /*
0002     SPDX-FileCopyrightText: 2006 Till Adam <adam@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "itemhydratest.h"
0008 
0009 #include "item.h"
0010 #include <QDebug>
0011 #include <QSharedPointer>
0012 #include <QTemporaryFile>
0013 #include <QTest>
0014 #include <memory>
0015 
0016 using namespace Akonadi;
0017 
0018 struct Volker {
0019     bool operator==(const Volker &f) const
0020     {
0021         return f.who == who;
0022     }
0023     virtual ~Volker()
0024     {
0025     }
0026     virtual Volker *clone() const = 0;
0027     QString who;
0028 };
0029 using VolkerPtr = std::shared_ptr<Volker>;
0030 using VolkerQPtr = QSharedPointer<Volker>;
0031 
0032 struct Rudi : public Volker {
0033     Rudi()
0034     {
0035         who = QStringLiteral("Rudi");
0036     }
0037     ~Rudi() override
0038     {
0039     }
0040     Rudi *clone() const override
0041     {
0042         return new Rudi(*this);
0043     }
0044 };
0045 
0046 using RudiPtr = std::shared_ptr<Rudi>;
0047 using RudiQPtr = QSharedPointer<Rudi>;
0048 
0049 struct Gerd : public Volker {
0050     Gerd()
0051     {
0052         who = QStringLiteral("Gerd");
0053     }
0054     Gerd *clone() const override
0055     {
0056         return new Gerd(*this);
0057     }
0058 
0059     using SuperClass = Volker;
0060 };
0061 
0062 using GerdPtr = std::shared_ptr<Gerd>;
0063 using GerdQPtr = QSharedPointer<Gerd>;
0064 
0065 Q_DECLARE_METATYPE(Volker *)
0066 Q_DECLARE_METATYPE(Rudi *)
0067 Q_DECLARE_METATYPE(Gerd *)
0068 
0069 Q_DECLARE_METATYPE(Rudi)
0070 Q_DECLARE_METATYPE(Gerd)
0071 
0072 namespace Akonadi
0073 {
0074 template<>
0075 struct SuperClass<Rudi> : public SuperClassTrait<Volker> {
0076 };
0077 }
0078 
0079 QTEST_MAIN(ItemHydra)
0080 
0081 ItemHydra::ItemHydra()
0082 {
0083 }
0084 
0085 void ItemHydra::initTestCase()
0086 {
0087 }
0088 
0089 void ItemHydra::testItemValuePayload()
0090 {
0091     Item f;
0092     Rudi rudi;
0093     f.setPayload(rudi);
0094     QVERIFY(f.hasPayload());
0095 
0096     Item b;
0097     Gerd gerd;
0098     b.setPayload(gerd);
0099     QVERIFY(b.hasPayload());
0100 
0101     QCOMPARE(f.payload<Rudi>(), rudi);
0102     QVERIFY(!(f.payload<Rudi>() == gerd));
0103     QCOMPARE(b.payload<Gerd>(), gerd);
0104     QVERIFY(!(b.payload<Gerd>() == rudi));
0105 }
0106 
0107 void ItemHydra::testItemPointerPayload()
0108 {
0109     Item f;
0110     Rudi *rudi = new Rudi;
0111 
0112     // the below should not compile
0113     // f.setPayload( rudi );
0114 
0115     // f.setPayload( std::unique_ptr<Rudi>( rudi ) );
0116     // QVERIFY( f.hasPayload() );
0117     // QCOMPARE( f.payload< std::unique_ptr<Rudi> >()->who, rudi->who );
0118 
0119     // below doesn't compile, hopefully
0120     // QCOMPARE( f.payload< Rudi* >()->who, rudi->who );
0121 
0122     delete rudi;
0123 }
0124 
0125 void ItemHydra::testItemCopy()
0126 {
0127     Item f;
0128     Rudi rudi;
0129     f.setPayload(rudi);
0130 
0131     Item r = f;
0132     QCOMPARE(r.payload<Rudi>(), rudi);
0133 
0134     Item s;
0135     s = f;
0136     QVERIFY(s.hasPayload());
0137     QCOMPARE(s.payload<Rudi>(), rudi);
0138 }
0139 
0140 void ItemHydra::testEmptyPayload()
0141 {
0142     Item i1;
0143     Item i2;
0144     i1 = i2; // should not crash
0145 
0146     QVERIFY(!i1.hasPayload());
0147     QVERIFY(!i2.hasPayload());
0148     QVERIFY(!i1.hasPayload<Rudi>());
0149     QVERIFY(!i1.hasPayload<RudiPtr>());
0150 
0151     bool caughtException = false;
0152     bool caughtRightException = true;
0153     try {
0154         Rudi r = i1.payload<Rudi>();
0155     } catch (const Akonadi::PayloadException &e) {
0156         qDebug() << e.what();
0157         caughtException = true;
0158         caughtRightException = true;
0159     } catch (const Akonadi::Exception &e) {
0160         qDebug() << "Caught Akonadi exception of type " << typeid(e).name() << ": " << e.what() << ", expected type"
0161                  << typeid(Akonadi::PayloadException).name();
0162         caughtException = true;
0163         caughtRightException = false;
0164     } catch (const std::exception &e) {
0165         qDebug() << "Caught exception of type " << typeid(e).name() << ": " << e.what() << ", expected type" << typeid(Akonadi::PayloadException).name();
0166         caughtException = true;
0167         caughtRightException = false;
0168     } catch (...) {
0169         qDebug() << "Caught unknown exception";
0170         caughtException = true;
0171         caughtRightException = false;
0172     }
0173     QVERIFY(caughtException);
0174     QVERIFY(caughtRightException);
0175 }
0176 
0177 void ItemHydra::testPointerPayload()
0178 {
0179     Rudi *r = new Rudi;
0180     RudiPtr p(r);
0181     std::weak_ptr<Rudi> w(p);
0182     QCOMPARE(p.use_count(), (long)1);
0183 
0184     {
0185         Item i1;
0186         i1.setPayload(p);
0187         QVERIFY(i1.hasPayload());
0188         QCOMPARE(p.use_count(), (long)2);
0189         {
0190             QVERIFY(i1.hasPayload<RudiPtr>());
0191             auto p2 = i1.payload<RudiPtr>();
0192             QCOMPARE(p.use_count(), (long)3);
0193         }
0194 
0195         {
0196             QVERIFY(i1.hasPayload<VolkerPtr>());
0197             auto p2 = i1.payload<VolkerPtr>();
0198             QCOMPARE(p.use_count(), (long)3);
0199         }
0200 
0201         QCOMPARE(p.use_count(), (long)2);
0202     }
0203     QCOMPARE(p.use_count(), (long)1);
0204     QCOMPARE(w.use_count(), (long)1);
0205     p.reset();
0206     QCOMPARE(w.use_count(), (long)0);
0207 }
0208 
0209 void ItemHydra::testPolymorphicPayloadWithTrait()
0210 {
0211     VolkerPtr p(new Rudi);
0212 
0213     {
0214         Item i1;
0215         i1.setPayload(p);
0216         QVERIFY(i1.hasPayload());
0217         QVERIFY(i1.hasPayload<VolkerPtr>());
0218         QVERIFY(i1.hasPayload<RudiPtr>());
0219         QVERIFY(!i1.hasPayload<GerdPtr>());
0220         QCOMPARE(p.use_count(), (long)2);
0221         {
0222             RudiPtr p2 = std::dynamic_pointer_cast<Rudi, Volker>(i1.payload<VolkerPtr>());
0223             QCOMPARE(p.use_count(), (long)3);
0224             QCOMPARE(p2->who, QStringLiteral("Rudi"));
0225         }
0226 
0227         {
0228             auto p2 = i1.payload<RudiPtr>();
0229             QCOMPARE(p.use_count(), (long)3);
0230             QCOMPARE(p2->who, QStringLiteral("Rudi"));
0231         }
0232 
0233         bool caughtException = false;
0234         try {
0235             auto p3 = i1.payload<GerdPtr>();
0236         } catch (const Akonadi::PayloadException &e) {
0237             qDebug() << e.what();
0238             caughtException = true;
0239         }
0240         QVERIFY(caughtException);
0241 
0242         QCOMPARE(p.use_count(), (long)2);
0243     }
0244 }
0245 
0246 void ItemHydra::testPolymorphicPayloadWithTypedef()
0247 {
0248     VolkerPtr p(new Gerd);
0249 
0250     {
0251         Item i1;
0252         i1.setPayload(p);
0253         QVERIFY(i1.hasPayload());
0254         QVERIFY(i1.hasPayload<VolkerPtr>());
0255         QVERIFY(!i1.hasPayload<RudiPtr>());
0256         QVERIFY(i1.hasPayload<GerdPtr>());
0257         QCOMPARE(p.use_count(), (long)2);
0258         {
0259             auto p2 = std::dynamic_pointer_cast<Gerd, Volker>(i1.payload<VolkerPtr>());
0260             QCOMPARE(p.use_count(), (long)3);
0261             QCOMPARE(p2->who, QStringLiteral("Gerd"));
0262         }
0263 
0264         {
0265             auto p2 = i1.payload<GerdPtr>();
0266             QCOMPARE(p.use_count(), (long)3);
0267             QCOMPARE(p2->who, QStringLiteral("Gerd"));
0268         }
0269 
0270         bool caughtException = false;
0271         try {
0272             auto p3 = i1.payload<RudiPtr>();
0273         } catch (const Akonadi::PayloadException &e) {
0274             qDebug() << e.what();
0275             caughtException = true;
0276         }
0277         QVERIFY(caughtException);
0278 
0279         QCOMPARE(p.use_count(), (long)2);
0280     }
0281 }
0282 
0283 void ItemHydra::testNullPointerPayload()
0284 {
0285     RudiPtr p(static_cast<Rudi *>(nullptr));
0286     Item i;
0287     i.setPayload(p);
0288     QVERIFY(i.hasPayload());
0289     QVERIFY(i.hasPayload<RudiPtr>());
0290     QVERIFY(i.hasPayload<VolkerPtr>());
0291     // Fails, because GerdQPtr is QSharedPointer, while RudiPtr is std::shared_ptr
0292     // and we cannot do sharedptr casting for null pointers
0293     QVERIFY(!i.hasPayload<GerdQPtr>());
0294     QCOMPARE(i.payload<RudiPtr>().get(), (Rudi *)nullptr);
0295     QCOMPARE(i.payload<VolkerPtr>().get(), (Volker *)nullptr);
0296 }
0297 
0298 void ItemHydra::testQSharedPointerPayload()
0299 {
0300     RudiQPtr p(new Rudi);
0301     Item i;
0302     i.setPayload(p);
0303     QVERIFY(i.hasPayload());
0304     QVERIFY(i.hasPayload<VolkerQPtr>());
0305     QVERIFY(i.hasPayload<RudiQPtr>());
0306     QVERIFY(!i.hasPayload<GerdQPtr>());
0307 
0308     {
0309         auto p2 = i.payload<VolkerQPtr>();
0310         QCOMPARE(p2->who, QStringLiteral("Rudi"));
0311     }
0312 
0313     {
0314         auto p2 = i.payload<RudiQPtr>();
0315         QCOMPARE(p2->who, QStringLiteral("Rudi"));
0316     }
0317 
0318     bool caughtException = false;
0319     try {
0320         auto p3 = i.payload<GerdQPtr>();
0321     } catch (const Akonadi::PayloadException &e) {
0322         qDebug() << e.what();
0323         caughtException = true;
0324     }
0325     QVERIFY(caughtException);
0326 }
0327 
0328 void ItemHydra::testHasPayload()
0329 {
0330     Item i1;
0331     QVERIFY(!i1.hasPayload<Rudi>());
0332     QVERIFY(!i1.hasPayload<Gerd>());
0333 
0334     Rudi r;
0335     i1.setPayload(r);
0336     QVERIFY(i1.hasPayload<Rudi>());
0337     QVERIFY(!i1.hasPayload<Gerd>());
0338 }
0339 
0340 void ItemHydra::testSharedPointerConversions()
0341 {
0342     Item a;
0343     RudiQPtr rudi(new Rudi);
0344     a.setPayload(rudi);
0345     // only the root base classes should show up with their metatype ids:
0346     QVERIFY(a.availablePayloadMetaTypeIds().contains(qMetaTypeId<Volker *>()));
0347     QVERIFY(a.hasPayload<RudiQPtr>());
0348     QVERIFY(a.hasPayload<VolkerPtr>());
0349     QVERIFY(a.hasPayload<RudiPtr>());
0350     QVERIFY(!a.hasPayload<GerdPtr>());
0351     QVERIFY(a.payload<RudiPtr>().get());
0352     QVERIFY(a.payload<VolkerPtr>().get());
0353     bool thrown = false;
0354 
0355     bool thrownCorrectly = true;
0356     try {
0357         QVERIFY(!a.payload<GerdPtr>());
0358     } catch (const Akonadi::PayloadException &e) {
0359         thrown = thrownCorrectly = true;
0360     } catch (...) {
0361         thrown = true;
0362         thrownCorrectly = false;
0363     }
0364     QVERIFY(thrown);
0365     QVERIFY(thrownCorrectly);
0366 }
0367 
0368 void ItemHydra::testForeignPayload()
0369 {
0370     QTemporaryFile file;
0371     QVERIFY(file.open());
0372     file.write("123456789");
0373     file.close();
0374 
0375     Item a(QStringLiteral("application/octet-stream"));
0376     a.setPayloadPath(file.fileName());
0377     QVERIFY(a.hasPayload<QByteArray>());
0378     QCOMPARE(a.payload<QByteArray>(), QByteArray("123456789"));
0379 
0380     Item b(QStringLiteral("application/octet-stream"));
0381     b.apply(a);
0382     QVERIFY(b.hasPayload<QByteArray>());
0383     QCOMPARE(b.payload<QByteArray>(), QByteArray("123456789"));
0384     QCOMPARE(b.payloadPath(), file.fileName());
0385 
0386     Item c = b;
0387     QVERIFY(c.hasPayload<QByteArray>());
0388     QCOMPARE(c.payload<QByteArray>(), QByteArray("123456789"));
0389     QCOMPARE(c.payloadPath(), file.fileName());
0390 }
0391 
0392 #include "moc_itemhydratest.cpp"