File indexing completed on 2024-06-16 04:23:16

0001 /*
0002     SPDX-FileCopyrightText: 2011-2013 Milian Wolff <mail@milianw.de>
0003     SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
0004     SPDX-FileCopyrightText: 2007-2009 David Nolden <david.nolden.kdevelop@art-master.de>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "test_duchain.h"
0010 
0011 #include <QTest>
0012 #include <QElapsedTimer>
0013 
0014 #include <tests/autotestshell.h>
0015 #include <tests/testcore.h>
0016 
0017 #include <language/duchain/duchain.h>
0018 #include <language/duchain/duchainlock.h>
0019 #include <language/duchain/persistentsymboltable.h>
0020 #include <language/duchain/codemodel.h>
0021 #include <language/duchain/types/typesystemdata.h>
0022 #include <language/duchain/types/integraltype.h>
0023 #include <language/duchain/types/typeregister.h>
0024 #include <language/duchain/declarationdata.h>
0025 #include <language/duchain/duchainregister.h>
0026 #include <language/duchain/problem.h>
0027 #include <language/duchain/parsingenvironment.h>
0028 
0029 #include <language/codegen/coderepresentation.h>
0030 
0031 #include <language/util/setrepository.h>
0032 #include <language/util/basicsetrepository.h>
0033 
0034 // #include <typeinfo>
0035 #include <set>
0036 #include <algorithm>
0037 #include <iterator> // needed for std::insert_iterator on windows
0038 #include <type_traits>
0039 #include <QThread>
0040 
0041 //Extremely slow
0042 // #define TEST_NORMAL_IMPORTS
0043 
0044 QTEST_MAIN(TestDUChain)
0045 
0046 using namespace KDevelop;
0047 using namespace Utils;
0048 
0049 static_assert(std::is_nothrow_move_assignable<TypePtr<AbstractType>>(), "Why would a move assignment operator throw?");
0050 static_assert(std::is_nothrow_move_constructible<TypePtr<AbstractType>>(), "Why would a move constructor throw?");
0051 
0052 static_assert(std::is_nothrow_move_assignable<DUChainPointer<DUContext>>(), "Why would a move assignment operator throw?");
0053 static_assert(std::is_nothrow_move_constructible<DUChainPointer<DUContext>>(), "Why would a move constructor throw?");
0054 
0055 using Index = BasicSetRepository::Index;
0056 
0057 struct Timer
0058 {
0059     Timer()
0060     {
0061         m_timer.start();
0062     }
0063     qint64 elapsed()
0064     {
0065         return m_timer.nsecsElapsed();
0066     }
0067     QElapsedTimer m_timer;
0068 };
0069 
0070 void TestDUChain::initTestCase()
0071 {
0072     AutoTestShell::init();
0073     TestCore::initialize(Core::NoUi);
0074 
0075     DUChain::self()->disablePersistentStorage();
0076     CodeRepresentation::setDiskChangesForbidden(true);
0077 }
0078 
0079 void TestDUChain::cleanupTestCase()
0080 {
0081     TestCore::shutdown();
0082 }
0083 
0084 #ifndef Q_OS_WIN
0085 void TestDUChain::testStringSets()
0086 {
0087     const unsigned int setCount = 8;
0088     const unsigned int choiceCount = 40;
0089     const unsigned int itemCount = 120;
0090 
0091     QRecursiveMutex mutex;
0092     BasicSetRepository rep(QStringLiteral("test repository"), &mutex);
0093 
0094 //  qDebug() << "Start repository-layout: \n" << rep.dumpDotGraph();
0095 
0096     qint64 repositoryTime = 0; //Time spent on repository-operations
0097     qint64 genericTime = 0; //Time spend on equivalent operations with generic sets
0098 
0099     qint64 repositoryIntersectionTime = 0; //Time spent on repository-operations
0100     qint64 genericIntersectionTime = 0; //Time spend on equivalent operations with generic sets
0101     qint64 qsetIntersectionTime = 0; //Time spend on equivalent operations with generic sets
0102 
0103     qint64 repositoryUnionTime = 0; //Time spent on repository-operations
0104     qint64 genericUnionTime = 0; //Time spend on equivalent operations with generic sets
0105 
0106     qint64 repositoryDifferenceTime = 0; //Time spent on repository-operations
0107     qint64 genericDifferenceTime = 0; //Time spend on equivalent operations with generic sets
0108 
0109     Set sets[setCount];
0110     std::set<Index> realSets[setCount];
0111     for (unsigned int a = 0; a < setCount; a++) {
0112         std::set<Index> chosenIndices;
0113         unsigned int thisCount = rand() % choiceCount;
0114         if (thisCount == 0)
0115             thisCount = 1;
0116 
0117         for (unsigned int b = 0; b < thisCount; b++) {
0118             Index choose = (rand() % itemCount) + 1;
0119             while (chosenIndices.find(choose) != chosenIndices.end()) {
0120                 choose = (rand() % itemCount) + 1;
0121             }
0122 
0123             Timer t;
0124             chosenIndices.insert(chosenIndices.end(), choose);
0125             genericTime += t.elapsed();
0126         }
0127 
0128         {
0129             Timer t;
0130             sets[a] = rep.createSet(chosenIndices);
0131             repositoryTime += t.elapsed();
0132         }
0133 
0134         realSets[a] = chosenIndices;
0135 
0136         const std::set<Index> tempSet = sets[a].stdSet();
0137 
0138         if (tempSet != realSets[a]) {
0139             QString dbg = QStringLiteral("created set: ");
0140             for (unsigned int i : qAsConst(realSets[a]))
0141                 dbg += QStringLiteral("%1 ").arg(i);
0142 
0143             qDebug() << dbg;
0144 
0145             dbg = QStringLiteral("repo.   set: ");
0146             for (unsigned int i : tempSet)
0147                 dbg += QStringLiteral("%1 ").arg(i);
0148 
0149             qDebug() << dbg;
0150 
0151             qDebug() << "DOT-Graph:\n\n" << sets[a].dumpDotGraph() << "\n\n";
0152             QFAIL("sets are not the same!");
0153         }
0154     }
0155 
0156     for (int cycle = 0; cycle < 100; ++cycle) {
0157         if (cycle % 10 == 0)
0158             qDebug() << "cycle" << cycle;
0159 
0160         for (unsigned int a = 0; a < setCount; a++) {
0161             for (unsigned int b = 0; b < setCount; b++) {
0162                 /// ----- SUBTRACTION/DIFFERENCE
0163                 std::set<Index> _realDifference;
0164                 {
0165                     Timer t;
0166                     std::set_difference(realSets[a].begin(), realSets[a].end(), realSets[b].begin(),
0167                                         realSets[b].end(),
0168                                         std::insert_iterator<std::set<Index>>(_realDifference,
0169                                                                               _realDifference.begin()));
0170                     genericDifferenceTime += t.elapsed();
0171                 }
0172 
0173                 Set _difference;
0174                 {
0175                     Timer t;
0176                     _difference = sets[a] - sets[b];
0177                     repositoryDifferenceTime += t.elapsed();
0178                 }
0179 
0180                 if (_difference.stdSet() != _realDifference) {
0181                     {
0182                         qDebug() << "SET a:";
0183                         QString dbg;
0184                         for (unsigned int i : qAsConst(realSets[a]))
0185                             dbg += QStringLiteral("%1 ").arg(i);
0186 
0187                         qDebug() << dbg;
0188 
0189                         qDebug() << "DOT-Graph:\n\n" << sets[a].dumpDotGraph() << "\n\n";
0190                     }
0191                     {
0192                         qDebug() << "SET b:";
0193                         QString dbg;
0194                         for (unsigned int i : qAsConst(realSets[b]))
0195                             dbg += QStringLiteral("%1 ").arg(i);
0196 
0197                         qDebug() << dbg;
0198 
0199                         qDebug() << "DOT-Graph:\n\n" << sets[b].dumpDotGraph() << "\n\n";
0200                     }
0201 
0202                     {
0203                         const std::set<Index> tempSet = _difference.stdSet();
0204 
0205                         qDebug() << "SET difference:";
0206                         QString dbg = QStringLiteral("real    set: ");
0207                         for (unsigned int i : qAsConst(_realDifference))
0208                             dbg += QStringLiteral("%1 ").arg(i);
0209 
0210                         qDebug() << dbg;
0211 
0212                         dbg = QStringLiteral("repo.   set: ");
0213                         for (unsigned int i : tempSet)
0214                             dbg += QStringLiteral("%1 ").arg(i);
0215 
0216                         qDebug() << dbg;
0217 
0218                         qDebug() << "DOT-Graph:\n\n" << _difference.dumpDotGraph() << "\n\n";
0219                     }
0220                     QFAIL("difference sets are not the same!");
0221                 }
0222 
0223                 /// ------ UNION
0224 
0225                 std::set<Index> _realUnion;
0226                 {
0227                     Timer t;
0228                     std::set_union(realSets[a].begin(), realSets[a].end(), realSets[b].begin(),
0229                                    realSets[b].end(),
0230                                    std::insert_iterator<std::set<Index>>(_realUnion, _realUnion.begin()));
0231                     genericUnionTime += t.elapsed();
0232                 }
0233 
0234                 Set _union;
0235                 {
0236                     Timer t;
0237                     _union = sets[a] + sets[b];
0238                     repositoryUnionTime += t.elapsed();
0239                 }
0240 
0241                 if (_union.stdSet() != _realUnion) {
0242                     {
0243                         qDebug() << "SET a:";
0244                         QString dbg;
0245                         for (unsigned int i : qAsConst(realSets[a]))
0246                             dbg += QStringLiteral("%1 ").arg(i);
0247 
0248                         qDebug() << dbg;
0249 
0250                         qDebug() << "DOT-Graph:\n\n" << sets[a].dumpDotGraph() << "\n\n";
0251                     }
0252                     {
0253                         qDebug() << "SET b:";
0254                         QString dbg;
0255                         for (unsigned int i : qAsConst(realSets[b]))
0256                             dbg += QStringLiteral("%1 ").arg(i);
0257 
0258                         qDebug() << dbg;
0259 
0260                         qDebug() << "DOT-Graph:\n\n" << sets[b].dumpDotGraph() << "\n\n";
0261                     }
0262 
0263                     {
0264                         const std::set<Index> tempSet = _union.stdSet();
0265 
0266                         qDebug() << "SET union:";
0267                         QString dbg = QStringLiteral("real    set: ");
0268                         for (unsigned int i : qAsConst(_realUnion))
0269                             dbg += QStringLiteral("%1 ").arg(i);
0270 
0271                         qDebug() << dbg;
0272 
0273                         dbg = QStringLiteral("repo.   set: ");
0274                         for (unsigned int i : tempSet)
0275                             dbg += QStringLiteral("%1 ").arg(i);
0276 
0277                         qDebug() << dbg;
0278 
0279                         qDebug() << "DOT-Graph:\n\n" << _union.dumpDotGraph() << "\n\n";
0280                     }
0281 
0282                     QFAIL("union sets are not the same");
0283                 }
0284 
0285                 std::set<Index> _realIntersection;
0286 
0287                 /// -------- INTERSECTION
0288                 {
0289                     Timer t;
0290                     std::set_intersection(realSets[a].begin(), realSets[a].end(), realSets[b].begin(),
0291                                           realSets[b].end(),
0292                                           std::insert_iterator<std::set<Index>>(_realIntersection,
0293                                                                                 _realIntersection.begin()));
0294                     genericIntersectionTime += t.elapsed();
0295                 }
0296 
0297                 //Just for fun: Test how fast QSet intersections are
0298                 QSet<Index> first, second;
0299                 for (unsigned int i : qAsConst(realSets[a])) {
0300                     first.insert(i);
0301                 }
0302 
0303                 for (unsigned int i : qAsConst(realSets[b])) {
0304                     second.insert(i);
0305                 }
0306 
0307                 {
0308                     Timer t;
0309                     QSet<Index> i = first.intersect(second); // clazy:exclude=unused-non-trivial-variable
0310                     qsetIntersectionTime += t.elapsed();
0311                 }
0312 
0313                 Set _intersection;
0314                 {
0315                     Timer t;
0316                     _intersection = sets[a] & sets[b];
0317                     repositoryIntersectionTime += t.elapsed();
0318                 }
0319 
0320                 if (_intersection.stdSet() != _realIntersection) {
0321                     {
0322                         qDebug() << "SET a:";
0323                         QString dbg;
0324                         for (unsigned int i : qAsConst(realSets[a]))
0325                             dbg += QStringLiteral("%1 ").arg(i);
0326 
0327                         qDebug() << dbg;
0328 
0329                         qDebug() << "DOT-Graph:\n\n" << sets[a].dumpDotGraph() << "\n\n";
0330                     }
0331                     {
0332                         qDebug() << "SET b:";
0333                         QString dbg;
0334                         for (unsigned int i : qAsConst(realSets[b]))
0335                             dbg += QStringLiteral("%1 ").arg(i);
0336 
0337                         qDebug() << dbg;
0338 
0339                         qDebug() << "DOT-Graph:\n\n" << sets[b].dumpDotGraph() << "\n\n";
0340                     }
0341 
0342                     {
0343                         const std::set<Index> tempSet = _intersection.stdSet();
0344 
0345                         qDebug() << "SET intersection:";
0346                         QString dbg = QStringLiteral("real    set: ");
0347                         for (unsigned int i : qAsConst(_realIntersection))
0348                             dbg += QStringLiteral("%1 ").arg(i);
0349 
0350                         qDebug() << dbg;
0351 
0352                         dbg = QStringLiteral("repo.   set: ");
0353                         for (unsigned int i : tempSet)
0354                             dbg += QStringLiteral("%1 ").arg(i);
0355 
0356                         qDebug() << dbg;
0357 
0358                         qDebug() << "DOT-Graph:\n\n" << _intersection.dumpDotGraph() << "\n\n";
0359                     }
0360                     QFAIL("intersection sets are not the same");
0361                 }
0362             }
0363         }
0364 
0365         qDebug() << "cycle " << cycle;
0366         qDebug() << "ns needed for set-building: repository-set: " << float( repositoryTime )
0367                  << " generic-set: " << float( genericTime );
0368         qDebug() << "ns needed for intersection: repository-sets: " << float( repositoryIntersectionTime )
0369                  << " generic-set: " << float( genericIntersectionTime ) << " QSet: " << float( qsetIntersectionTime );
0370         qDebug() << "ns needed for union: repository-sets: " << float( repositoryUnionTime )
0371                  << " generic-set: " << float( genericUnionTime );
0372         qDebug() << "ns needed for difference: repository-sets: " << float( repositoryDifferenceTime )
0373                  << " generic-set: " << float( genericDifferenceTime );
0374     }
0375 }
0376 #endif
0377 
0378 void TestDUChain::testSymbolTableValid()
0379 {
0380     DUChainReadLocker lock(DUChain::lock());
0381     PersistentSymbolTable::self().dump(QTextStream(stdout));
0382 }
0383 
0384 void TestDUChain::testIndexedStrings()
0385 {
0386     int testCount  = 600000;
0387 
0388     QHash<QString, IndexedString> knownIndices;
0389     int a = 0;
0390     for (a = 0; a < testCount; ++a) {
0391         QString testString;
0392         int length = rand() % 10;
0393         for (int b = 0; b < length; ++b)
0394             testString.append(( char )(rand() % 6) + 'a');
0395 
0396         QByteArray array = testString.toUtf8();
0397         //qDebug() << "checking with" << testString;
0398         //qDebug() << "checking" << a;
0399         IndexedString indexed(array.constData(), array.size(), IndexedString::hashString(array.constData(),
0400                                                                                          array.size()));
0401 
0402         QCOMPARE(indexed.str(), testString);
0403         if (knownIndices.contains(testString)) {
0404             QCOMPARE(indexed.index(), knownIndices[testString].index());
0405         } else {
0406             knownIndices[testString] = indexed;
0407         }
0408 
0409         if (a % (testCount / 10) == 0)
0410             qDebug() << a << "of" << testCount;
0411     }
0412 
0413     qDebug() << a << "successful tests";
0414 }
0415 
0416 struct TestContext
0417 {
0418     TestContext()
0419     {
0420         static int number = 0;
0421         ++number;
0422         DUChainWriteLocker lock(DUChain::lock());
0423         m_context = new TopDUContext(IndexedString(QStringLiteral("/test1/%1").arg(number)), RangeInRevision());
0424         m_normalContext = new DUContext(RangeInRevision(), m_context);
0425         DUChain::self()->addDocumentChain(m_context);
0426         Q_ASSERT(IndexedDUContext(m_context).context() == m_context);
0427     }
0428 
0429     ~TestContext()
0430     {
0431         const auto currentImporters = importers;
0432         for (TestContext* importer : currentImporters) {
0433             importer->unImport(QList<TestContext*>() << this);
0434         }
0435 
0436         unImport(imports);
0437 
0438         DUChainWriteLocker lock(DUChain::lock());
0439         TopDUContextPointer tp(m_context);
0440         DUChain::self()->removeDocumentChain(static_cast<TopDUContext*>(m_context));
0441         Q_ASSERT(!tp);
0442     }
0443 
0444     void verify(QList<TestContext*> allContexts)
0445     {
0446         {
0447             DUChainReadLocker lock(DUChain::lock());
0448             QCOMPARE(m_context->importedParentContexts().count(), imports.count());
0449         }
0450         //Compute a closure of all children, and verify that they are imported.
0451         QSet<TestContext*> collected;
0452         collectImports(collected);
0453         collected.remove(this);
0454 
0455         DUChainReadLocker lock(DUChain::lock());
0456         for (TestContext* context : qAsConst(collected)) {
0457             QVERIFY(m_context->imports(context->m_context, CursorInRevision::invalid()));
0458 #ifdef TEST_NORMAL_IMPORTS
0459             QVERIFY(m_normalContext->imports(context->m_normalContext));
0460 #endif
0461         }
0462 
0463         //Verify that no other contexts are imported
0464 
0465         for (TestContext* context : qAsConst(allContexts)) {
0466             if (context != this) {
0467                 QVERIFY(collected.contains(context) ||
0468                         !m_context->imports(context->m_context, CursorInRevision::invalid()));
0469 #ifdef TEST_NORMAL_IMPORTS
0470                 QVERIFY(collected.contains(context) ||
0471                         !m_normalContext->imports(context->m_normalContext, CursorInRevision::invalid()));
0472 #endif
0473             }
0474         }
0475     }
0476 
0477     void collectImports(QSet<TestContext*>& collected)
0478     {
0479         if (collected.contains(this))
0480             return;
0481         collected.insert(this);
0482         for (TestContext* context : qAsConst(imports)) {
0483             context->collectImports(collected);
0484         }
0485     }
0486     void import(TestContext* ctx)
0487     {
0488         if (imports.contains(ctx) || ctx == this)
0489             return;
0490         imports << ctx;
0491         ctx->importers << this;
0492         DUChainWriteLocker lock(DUChain::lock());
0493         m_context->addImportedParentContext(ctx->m_context);
0494 #ifdef TEST_NORMAL_IMPORTS
0495         m_normalContext->addImportedParentContext(ctx->m_normalContext);
0496 #endif
0497     }
0498 
0499     void unImport(QList<TestContext*> ctxList)
0500     {
0501         QList<TopDUContext*> list;
0502         QList<DUContext*> normalList;
0503 
0504         for (TestContext* ctx : qAsConst(ctxList)) {
0505             if (!imports.contains(ctx))
0506                 continue;
0507             list << ctx->m_context;
0508             normalList << ctx->m_normalContext;
0509 
0510             imports.removeAll(ctx);
0511             ctx->importers.removeAll(this);
0512         }
0513 
0514         DUChainWriteLocker lock(DUChain::lock());
0515         m_context->removeImportedParentContexts(list);
0516 
0517 #ifdef TEST_NORMAL_IMPORTS
0518         for (DUContext* ctx : qAsConst(normalList)) {
0519             m_normalContext->removeImportedParentContext(ctx);
0520         }
0521 
0522 #endif
0523     }
0524 
0525     void clearImports()
0526     {
0527         {
0528             DUChainWriteLocker lock(DUChain::lock());
0529 
0530             m_context->clearImportedParentContexts();
0531             m_normalContext->clearImportedParentContexts();
0532         }
0533         const auto currentImports = imports;
0534         for (TestContext* ctx : currentImports) {
0535             imports.removeAll(ctx);
0536             ctx->importers.removeAll(this);
0537         }
0538     }
0539 
0540     QList<TestContext*> imports;
0541 
0542 private:
0543 
0544     TopDUContext* m_context;
0545     DUContext* m_normalContext;
0546     QList<TestContext*> importers;
0547 };
0548 
0549 void collectReachableNodes(QSet<uint>& reachableNodes, uint currentNode)
0550 {
0551     if (!currentNode)
0552         return;
0553     reachableNodes.insert(currentNode);
0554     const Utils::SetNodeData* node = KDevelop::RecursiveImportRepository::repository()->nodeFromIndex(currentNode);
0555     Q_ASSERT(node);
0556     collectReachableNodes(reachableNodes, node->leftNode());
0557     collectReachableNodes(reachableNodes, node->rightNode());
0558 }
0559 
0560 uint collectNaiveNodeCount(uint currentNode)
0561 {
0562     if (!currentNode)
0563         return 0;
0564     uint ret = 1;
0565     const Utils::SetNodeData* node = KDevelop::RecursiveImportRepository::repository()->nodeFromIndex(currentNode);
0566     Q_ASSERT(node);
0567     ret += collectNaiveNodeCount(node->leftNode());
0568     ret += collectNaiveNodeCount(node->rightNode());
0569     return ret;
0570 }
0571 
0572 void TestDUChain::testImportStructure()
0573 {
0574     Timer total;
0575     qDebug() << "before: " << KDevelop::RecursiveImportRepository::repository()->statistics().print();
0576 
0577     ///Maintains a naive import-structure along with a real top-context import structure, and allows comparing both.
0578     int cycles = 5;
0579     //int cycles = 100;
0580     //srand(time(NULL));
0581     for (int t = 0; t < cycles; ++t) {
0582         QList<TestContext*> allContexts;
0583         //Create a random structure
0584         int contextCount = 50;
0585         int verifyOnceIn = contextCount /*((contextCount*contextCount)/20)+1*/; //Verify once in every chances(not in all cases, because else the import-structure isn't built on-demand!)
0586         int clearOnceIn = contextCount;
0587         for (int a = 0; a < contextCount; a++)
0588             allContexts << new TestContext();
0589 
0590         for (int c = 0; c < cycles; ++c) {
0591             //qDebug() << "main-cycle" << t  << "sub-cycle" << c;
0592             //Add random imports and compare
0593             for (int a = 0; a < contextCount; a++) {
0594                 //Import up to 5 random other contexts into each context
0595                 int importCount = rand() % 5;
0596                 //qDebug()   << "cnt> " << importCount;
0597                 for (int i = 0; i < importCount; ++i) {
0598                     //int importNr = rand() % contextCount;
0599                     //qDebug() << "nmr > " << importNr;
0600                     //allContexts[a]->import(allContexts[importNr]);
0601                     allContexts[a]->import(allContexts[rand() % contextCount]);
0602                 }
0603 
0604                 for (int b = 0; b < contextCount; b++)
0605                     if (rand() % verifyOnceIn == 0)
0606                         allContexts[b]->verify(allContexts);
0607             }
0608 
0609             //Remove random imports and compare
0610             for (int a = 0; a < contextCount; a++) {
0611                 //Import up to 5 random other contexts into each context
0612                 int removeCount = rand() % 3;
0613                 QSet<TestContext*> removeImports;
0614                 for (int i = 0; i < removeCount; ++i)
0615                     if (!allContexts[a]->imports.isEmpty())
0616                         removeImports.insert(allContexts[a]->imports[rand() % allContexts[a]->imports.count()]);
0617 
0618                 allContexts[a]->unImport(removeImports.values());
0619 
0620                 for (int b = 0; b < contextCount; b++)
0621                     if (rand() % verifyOnceIn == 0)
0622                         allContexts[b]->verify(allContexts);
0623             }
0624 
0625             for (int a = 0; a < contextCount; a++) {
0626                 if (rand() % clearOnceIn == 0) {
0627                     allContexts[a]->clearImports();
0628                     allContexts[a]->verify(allContexts);
0629                 }
0630             }
0631         }
0632 
0633         qDebug() << "after: " <<
0634             KDevelop::RecursiveImportRepository::repository()->statistics().print();
0635 
0636         for (int a = 0; a < contextCount; ++a)
0637             delete allContexts[a];
0638 
0639         allContexts.clear();
0640         qDebug() << "after cleanup: " <<
0641             KDevelop::RecursiveImportRepository::repository()->statistics().print();
0642     }
0643 
0644     qDebug() << "total ns needed for import-structure test:" << float( total.elapsed());
0645 }
0646 
0647 class TestWorker
0648     : public QObject
0649 {
0650     Q_OBJECT
0651 
0652 public Q_SLOTS:
0653     void lockForWrite()
0654     {
0655         for (int i = 0; i < 10000; ++i) {
0656             DUChainWriteLocker lock;
0657         }
0658     }
0659     void lockForRead()
0660     {
0661         for (int i = 0; i < 10000; ++i) {
0662             DUChainReadLocker lock;
0663         }
0664     }
0665     void lockForReadWrite()
0666     {
0667         for (int i = 0; i < 10000; ++i) {
0668             {
0669                 DUChainReadLocker lock;
0670             }
0671             {
0672                 DUChainWriteLocker lock;
0673             }
0674         }
0675     }
0676     static QSharedPointer<QThread> createWorkerThread(const char* workerSlot)
0677     {
0678         auto* thread = new QThread;
0679         auto* worker = new TestWorker;
0680         connect(thread, SIGNAL(started()), worker, workerSlot);
0681         connect(thread, &QThread::finished, worker, &TestWorker::deleteLater);
0682         worker->moveToThread(thread);
0683         return QSharedPointer<QThread>(thread);
0684     }
0685 };
0686 
0687 class ThreadList
0688     : public QVector<QSharedPointer<QThread>>
0689 {
0690 public:
0691     bool join(int timeout)
0692     {
0693         for (const QSharedPointer<QThread>& thread : qAsConst(*this)) {
0694             // quit event loop
0695             Q_ASSERT(thread->isRunning());
0696             thread->quit();
0697             // wait for finish
0698             if (!thread->wait(timeout)) {
0699                 return false;
0700             }
0701             Q_ASSERT(thread->isFinished());
0702         }
0703 
0704         return true;
0705     }
0706     void start()
0707     {
0708         for (const QSharedPointer<QThread>& thread : qAsConst(*this)) {
0709             thread->start();
0710         }
0711     }
0712 };
0713 
0714 void TestDUChain::testLockForWrite()
0715 {
0716     ThreadList threads;
0717     for (int i = 0; i < 10; ++i) {
0718         threads << TestWorker::createWorkerThread(SLOT(lockForWrite()));
0719     }
0720 
0721     threads.start();
0722     QBENCHMARK {
0723         {
0724             DUChainWriteLocker lock;
0725         }
0726         {
0727             DUChainReadLocker lock;
0728         }
0729     }
0730     QVERIFY(threads.join(1000));
0731 }
0732 
0733 void TestDUChain::testLockForRead()
0734 {
0735     ThreadList threads;
0736     for (int i = 0; i < 10; ++i) {
0737         threads << TestWorker::createWorkerThread(SLOT(lockForRead()));
0738     }
0739 
0740     threads.start();
0741     QBENCHMARK {
0742         DUChainReadLocker lock;
0743     }
0744     QVERIFY(threads.join(1000));
0745 }
0746 
0747 void TestDUChain::testLockForReadWrite()
0748 {
0749     ThreadList threads;
0750     for (int i = 0; i < 10; ++i) {
0751         threads << TestWorker::createWorkerThread(SLOT(lockForReadWrite()));
0752     }
0753 
0754     threads.start();
0755     QBENCHMARK {
0756         DUChainWriteLocker lock;
0757     }
0758     QVERIFY(threads.join(1000));
0759 }
0760 
0761 void TestDUChain::testProblemSerialization()
0762 {
0763     DUChain::self()->disablePersistentStorage(false);
0764 
0765     auto parent = ProblemPointer{new Problem};
0766     parent->setDescription(QStringLiteral("parent"));
0767 
0768     auto child = ProblemPointer{new Problem};
0769     child->setDescription(QStringLiteral("child"));
0770     parent->addDiagnostic(child);
0771 
0772     const IndexedString url("/my/test/file");
0773 
0774     TopDUContextPointer smartTop;
0775 
0776     { // serialize
0777         DUChainWriteLocker lock;
0778         auto file = new ParsingEnvironmentFile(url);
0779         auto top = new TopDUContext(url, {}, file);
0780 
0781         top->addProblem(parent);
0782         QCOMPARE(top->problems().size(), 1);
0783         auto p = top->problems().at(0);
0784         QCOMPARE(p->description(), QStringLiteral("parent"));
0785         QCOMPARE(p->diagnostics().size(), 1);
0786         auto c = p->diagnostics().first();
0787         QCOMPARE(c->description(), QStringLiteral("child"));
0788 
0789         DUChain::self()->addDocumentChain(top);
0790         QVERIFY(DUChain::self()->chainForDocument(url));
0791         smartTop = top;
0792     }
0793 
0794     DUChain::self()->storeToDisk();
0795 
0796     ProblemPointer parent_deserialized;
0797     IProblem::Ptr child_deserialized;
0798 
0799     { // deserialize
0800         DUChainWriteLocker lock;
0801         QVERIFY(!smartTop);
0802         auto top = DUChain::self()->chainForDocument(url);
0803         QVERIFY(top);
0804         smartTop = top;
0805         QCOMPARE(top->problems().size(), 1);
0806         parent_deserialized = top->problems().at(0);
0807         QCOMPARE(parent_deserialized->diagnostics().size(), 1);
0808         child_deserialized = parent_deserialized->diagnostics().first();
0809 
0810         QCOMPARE(parent_deserialized->description(), QStringLiteral("parent"));
0811         QCOMPARE(child_deserialized->description(), QStringLiteral("child"));
0812 
0813         top->clearProblems();
0814         QVERIFY(top->problems().isEmpty());
0815 
0816         QCOMPARE(parent_deserialized->description(), QStringLiteral("parent"));
0817         QCOMPARE(child_deserialized->description(), QStringLiteral("child"));
0818 
0819         DUChain::self()->removeDocumentChain(top);
0820 
0821         QCOMPARE(parent_deserialized->description(), QStringLiteral("parent"));
0822         QCOMPARE(child_deserialized->description(), QStringLiteral("child"));
0823 
0824         QVERIFY(!smartTop);
0825     }
0826 
0827     DUChain::self()->disablePersistentStorage(true);
0828 
0829     QCOMPARE(parent->description(), QStringLiteral("parent"));
0830     QCOMPARE(child->description(), QStringLiteral("child"));
0831     QCOMPARE(parent_deserialized->description(), QStringLiteral("parent"));
0832     QCOMPARE(child_deserialized->description(), QStringLiteral("child"));
0833 
0834     parent->clearDiagnostics();
0835     QVERIFY(parent->diagnostics().isEmpty());
0836 }
0837 
0838 void TestDUChain::testIdentifiers()
0839 {
0840     QualifiedIdentifier aj(QStringLiteral("::Area::jump"));
0841     QCOMPARE(aj.count(), 2);
0842     QCOMPARE(aj.explicitlyGlobal(), true);
0843     QCOMPARE(aj.at(0), Identifier(QStringLiteral("Area")));
0844     QCOMPARE(aj.at(1), Identifier(QStringLiteral("jump")));
0845 
0846     QualifiedIdentifier aj2 = QualifiedIdentifier(QStringLiteral("Area::jump"));
0847     QCOMPARE(aj2.count(), 2);
0848     QCOMPARE(aj2.explicitlyGlobal(), false);
0849     QCOMPARE(aj2.at(0), Identifier(QStringLiteral("Area")));
0850     QCOMPARE(aj2.at(1), Identifier(QStringLiteral("jump")));
0851     QVERIFY(aj != aj2);
0852 
0853     QVERIFY(QualifiedIdentifier(QString()) == QualifiedIdentifier());
0854     QVERIFY(QualifiedIdentifier(QString()).index() == QualifiedIdentifier().index());
0855 
0856     QualifiedIdentifier ajt(QStringLiteral("Area::jump::test"));
0857     QualifiedIdentifier jt(QStringLiteral("jump::test"));
0858     QualifiedIdentifier ajt2(QStringLiteral("Area::jump::tes"));
0859 
0860     QualifiedIdentifier t(QStringLiteral(" Area<A,B>::jump <F> ::tes<C>"));
0861     QCOMPARE(t.count(), 3);
0862     QCOMPARE(t.at(0).templateIdentifiersCount(), 2u);
0863     QCOMPARE(t.at(1).templateIdentifiersCount(), 1u);
0864     QCOMPARE(t.at(2).templateIdentifiersCount(), 1u);
0865     QCOMPARE(t.at(0).identifier().str(), QStringLiteral("Area"));
0866     QCOMPARE(t.at(1).identifier().str(), QStringLiteral("jump"));
0867     QCOMPARE(t.at(2).identifier().str(), QStringLiteral("tes"));
0868 
0869     QualifiedIdentifier op1(QStringLiteral("operator<"));
0870     QualifiedIdentifier op2(QStringLiteral("operator<="));
0871     QualifiedIdentifier op3(QStringLiteral("operator>"));
0872     QualifiedIdentifier op4(QStringLiteral("operator>="));
0873     QualifiedIdentifier op5(QStringLiteral("operator()"));
0874     QualifiedIdentifier op6(QStringLiteral("operator( )"));
0875     QCOMPARE(op1.count(), 1);
0876     QCOMPARE(op2.count(), 1);
0877     QCOMPARE(op3.count(), 1);
0878     QCOMPARE(op4.count(), 1);
0879     QCOMPARE(op5.count(), 1);
0880     QCOMPARE(op6.count(), 1);
0881     QCOMPARE(op4.toString(), QStringLiteral("operator>="));
0882     QCOMPARE(op3.toString(), QStringLiteral("operator>"));
0883     QCOMPARE(op1.toString(), QStringLiteral("operator<"));
0884     QCOMPARE(op2.toString(), QStringLiteral("operator<="));
0885     QCOMPARE(op5.toString(), QStringLiteral("operator()"));
0886     QCOMPARE(op6.toString(), QStringLiteral("operator( )"));
0887     QCOMPARE(QualifiedIdentifier(QStringLiteral("Area<A,B>::jump <F> ::tes<C>")).index(), t.index());
0888     QCOMPARE(op4.index(), QualifiedIdentifier(QStringLiteral("operator>=")).index());
0889 
0890     QualifiedIdentifier pushTest(QStringLiteral("foo"));
0891     QCOMPARE(pushTest.count(), 1);
0892     QCOMPARE(pushTest.toString(), QStringLiteral("foo"));
0893     pushTest.push(Identifier(QStringLiteral("bar")));
0894     QCOMPARE(pushTest.count(), 2);
0895     QCOMPARE(pushTest.toString(), QStringLiteral("foo::bar"));
0896     pushTest.push(QualifiedIdentifier(QStringLiteral("baz::asdf")));
0897     QCOMPARE(pushTest.count(), 4);
0898     QCOMPARE(pushTest.toString(), QStringLiteral("foo::bar::baz::asdf"));
0899     QualifiedIdentifier mergeTest = pushTest.merge(QualifiedIdentifier(QStringLiteral("meh::muh")));
0900     QCOMPARE(mergeTest.count(), 6);
0901     QCOMPARE(mergeTest.toString(), QStringLiteral("meh::muh::foo::bar::baz::asdf"));
0902     QualifiedIdentifier plusTest = QualifiedIdentifier(QStringLiteral("la::lu")) +
0903                                    QualifiedIdentifier(QStringLiteral("ba::bu"));
0904     QCOMPARE(plusTest.count(), 4);
0905     QCOMPARE(plusTest.toString(), QStringLiteral("la::lu::ba::bu"));
0906     ///@todo create a big randomized test for the identifier repository(check that indices are the same)
0907 }
0908 
0909 void TestDUChain::testTypePtr()
0910 {
0911     AbstractType::Ptr abstractT;
0912     QVERIFY(!abstractT);
0913 
0914     IntegralType::Ptr integralT(new IntegralType(IntegralType::TypeDouble));
0915 
0916     abstractT = integralT;
0917     QCOMPARE(abstractT, integralT);
0918 
0919     DelayedType::Ptr delayedT(new DelayedType);
0920     QVERIFY(abstractT != delayedT);
0921 
0922     auto abstractT2 = abstractT;
0923     QCOMPARE(abstractT2, integralT);
0924 
0925     auto abstractT3 = AbstractType::Ptr(integralT);
0926     QCOMPARE(abstractT2, integralT);
0927 }
0928 
0929 #if 0
0930 
0931 ///NOTE: the "unit tests" below are not automated, they - so far - require
0932 ///      human interpretation which is not useful for a unit test!
0933 ///      someone should investigate what the expected output should be
0934 ///      and add proper QCOMPARE/QVERIFY checks accordingly
0935 
0936 ///FIXME: this needs to be rewritten in order to remove dependencies on formerly run unit tests
0937 void TestDUChain::testImportCache()
0938 {
0939     KDevelop::globalItemRepositoryRegistry().printAllStatistics();
0940 
0941     KDevelop::RecursiveImportRepository::repository()->printStatistics();
0942 
0943     //Analyze the whole existing import-cache
0944     //This is very expensive, since it involves loading all existing top-contexts
0945     uint topContextCount = DUChain::self()->newTopContextIndex();
0946 
0947     uint analyzedCount = 0;
0948     uint totalImportCount = 0;
0949     uint naiveNodeCount = 0;
0950     QSet<uint> reachableNodes;
0951 
0952     DUChainReadLocker lock(DUChain::lock());
0953     for (uint a = 0; a < topContextCount; ++a) {
0954         if (a % qMax(1u, topContextCount / 100) == 0) {
0955             qDebug() << "progress:" << (a * 100) / topContextCount;
0956         }
0957         TopDUContext* context = DUChain::self()->chainForIndex(a);
0958         if (context) {
0959             TopDUContext::IndexedRecursiveImports imports = context->recursiveImportIndices();
0960             ++analyzedCount;
0961             totalImportCount += imports.set().count();
0962             collectReachableNodes(reachableNodes, imports.setIndex());
0963             naiveNodeCount += collectNaiveNodeCount(imports.setIndex());
0964         }
0965     }
0966 
0967     QVERIFY(analyzedCount);
0968     qDebug() << "average total count of imports:" << totalImportCount / analyzedCount;
0969     qDebug() << "count of reachable nodes:" << reachableNodes.size();
0970     qDebug() << "naive node-count:" << naiveNodeCount << "sharing compression factor:" <<
0971     (( float )reachableNodes.size()) / (( float )naiveNodeCount);
0972 }
0973 
0974 #endif
0975 
0976 void TestDUChain::benchCodeModel()
0977 {
0978     const IndexedString file("testFile");
0979 
0980     QVERIFY(!QTypeInfo<KDevelop::CodeModelItem>::isStatic);
0981 
0982     int i = 0;
0983     QBENCHMARK {
0984         CodeModel::self().addItem(file, QualifiedIdentifier("testQID" + QString::number(i++)),
0985                                   KDevelop::CodeModelItem::Class);
0986     }
0987 }
0988 
0989 void TestDUChain::benchTypeRegistry()
0990 {
0991     IntegralTypeData data;
0992     data.m_dataType = IntegralType::TypeInt;
0993     data.typeClassId = IntegralType::Identity;
0994     data.inRepository = true;
0995     data.m_modifiers = 42;
0996     data.m_dynamic = false;
0997     data.refCount = 1;
0998 
0999     IntegralTypeData to;
1000 
1001     QFETCH(int, func);
1002 
1003     QBENCHMARK {
1004         switch (func) {
1005         case 0:
1006             TypeSystem::self().dataClassSize(data);
1007             break;
1008         case 1:
1009             TypeSystem::self().dynamicSize(data);
1010             break;
1011         case 2: {
1012             AbstractType::Ptr t(TypeSystem::self().create(&data));
1013             break;
1014         }
1015         case 3:
1016             TypeSystem::self().isFactoryLoaded(data);
1017             break;
1018         case 4:
1019             TypeSystem::self().copy(data, to, !data.m_dynamic);
1020             break;
1021         case 5:
1022             TypeSystem::self().copy(data, to, data.m_dynamic);
1023             break;
1024         case 6:
1025             TypeSystem::self().callDestructor(&data);
1026             break;
1027         }
1028     }
1029 }
1030 
1031 void TestDUChain::benchTypeRegistry_data()
1032 {
1033     QTest::addColumn<int>("func");
1034     QTest::newRow("dataClassSize") << 0;
1035     QTest::newRow("dynamicSize") << 1;
1036     QTest::newRow("create") << 2;
1037     QTest::newRow("isFactoryLoaded") << 3;
1038     QTest::newRow("copy") << 4;
1039     QTest::newRow("copyNonDynamic") << 5;
1040     QTest::newRow("callDestructor") << 6;
1041 }
1042 
1043 void TestDUChain::benchDuchainReadLocker()
1044 {
1045     QBENCHMARK {
1046         DUChainReadLocker lock;
1047     }
1048 }
1049 
1050 void TestDUChain::benchDuchainWriteLocker()
1051 {
1052     QBENCHMARK {
1053         DUChainWriteLocker lock;
1054     }
1055 }
1056 
1057 void TestDUChain::benchDUChainItemFactory_copy()
1058 {
1059     DUChainItemFactory<Declaration, DeclarationData> factory;
1060     DeclarationData from, to;
1061     from.classId = Declaration::Identity;
1062 
1063     QFETCH(int, constant);
1064 
1065     bool c = constant;
1066 
1067     QBENCHMARK {
1068         factory.copy(from, to, c);
1069         if (constant == 2) {
1070             c = !c;
1071         }
1072     }
1073 }
1074 
1075 void TestDUChain::benchDUChainItemFactory_copy_data()
1076 {
1077     QTest::addColumn<int>("constant");
1078     QTest::newRow("non-const") << 0;
1079     QTest::newRow("const") << 1;
1080     QTest::newRow("flip") << 2;
1081 }
1082 
1083 void TestDUChain::benchDeclarationQualifiedIdentifier()
1084 {
1085     QVector<DUContext*> contexts;
1086     contexts.reserve(10);
1087     DUChainWriteLocker lock;
1088     auto topDUContext = new TopDUContext(IndexedString("/tmp/something"), {0, 0, INT_MAX, INT_MAX});
1089     DUChain::self()->addDocumentChain(topDUContext);
1090     contexts << topDUContext;
1091     for (int i = 1; i < contexts.capacity(); ++i) {
1092         contexts << new DUContext({0, 0, INT_MAX, INT_MAX}, contexts.at(i - 1));
1093         contexts.last()->setLocalScopeIdentifier(QualifiedIdentifier(QString::number(i)));
1094     }
1095 
1096     auto dec = new Declaration({0, 0, 0, 1}, contexts.last());
1097     dec->setIdentifier(Identifier(QStringLiteral("myDecl")));
1098 
1099     qDebug() << "start benchmark!";
1100     qint64 count = 0;
1101     QBENCHMARK {
1102         count += dec->qualifiedIdentifier().count();
1103     }
1104     QVERIFY(count > 0);
1105 
1106     // manually delete as QScopedPointer does not work well with QBENCHMARK
1107     delete dec;
1108     DUChain::self()->removeDocumentChain(topDUContext);
1109 }
1110 
1111 #include "test_duchain.moc"
1112 #include "moc_test_duchain.cpp"